OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.chrome.browser.partnercustomizations; |
| 6 |
| 7 import android.content.ContentResolver; |
| 8 import android.content.Context; |
| 9 import android.content.pm.ApplicationInfo; |
| 10 import android.content.pm.ProviderInfo; |
| 11 import android.database.Cursor; |
| 12 import android.net.Uri; |
| 13 import android.os.AsyncTask; |
| 14 import android.text.TextUtils; |
| 15 import android.util.Log; |
| 16 |
| 17 import org.chromium.base.ThreadUtils; |
| 18 import org.chromium.base.VisibleForTesting; |
| 19 import org.chromium.chrome.browser.partnerbookmarks.PartnerBookmarksReader; |
| 20 |
| 21 import java.util.ArrayList; |
| 22 import java.util.List; |
| 23 |
| 24 /** |
| 25 * Reads and caches partner browser customizations information if it exists. |
| 26 */ |
| 27 public class PartnerBrowserCustomizations { |
| 28 private static final String TAG = "PartnerBrowserProvider"; |
| 29 private static final String PROVIDER_AUTHORITY = "com.android.partnerbrowser
customizations"; |
| 30 |
| 31 // Private homepage structure. |
| 32 static final String PARTNER_HOMEPAGE_PATH = "homepage"; |
| 33 static final String PARTNER_DISABLE_BOOKMARKS_EDITING_PATH = "disablebookmar
ksediting"; |
| 34 @VisibleForTesting |
| 35 public static final String PARTNER_DISABLE_INCOGNITO_MODE_PATH = "disableinc
ognitomode"; |
| 36 |
| 37 private static String sProviderAuthority = PROVIDER_AUTHORITY; |
| 38 private static boolean sIgnoreBrowserProviderSystemPackageCheck = false; |
| 39 private static volatile String sHomepage; |
| 40 private static volatile boolean sIncognitoModeDisabled; |
| 41 private static volatile boolean sBookmarksEditingDisabled; |
| 42 private static boolean sIsInitialized; |
| 43 private static List<Runnable> sInitializeAsyncCallbacks = new ArrayList<Runn
able>(); |
| 44 |
| 45 /** |
| 46 * @return True if the partner homepage content provider exists and enabled.
Note that The data |
| 47 * this method reads is not initialized until the asynchronous initi
alization of this |
| 48 * class has been completed. |
| 49 */ |
| 50 public static boolean isHomepageProviderAvailableAndEnabled() { |
| 51 return !TextUtils.isEmpty(getHomePageUrl()); |
| 52 } |
| 53 |
| 54 /** |
| 55 * @return Whether incognito mode is disabled by the partner. |
| 56 */ |
| 57 public static boolean isIncognitoDisabled() { |
| 58 return sIncognitoModeDisabled; |
| 59 } |
| 60 |
| 61 /** |
| 62 * @return Whether partner bookmarks editing is disabled by the partner. |
| 63 */ |
| 64 @VisibleForTesting |
| 65 static boolean isBookmarksEditingDisabled() { |
| 66 return sBookmarksEditingDisabled; |
| 67 } |
| 68 |
| 69 /** |
| 70 * @return True, if initialization is finished. Checking that there is no pr
ovider, or failing |
| 71 * to read provider is also considered initialization. |
| 72 */ |
| 73 @VisibleForTesting |
| 74 static boolean isInitialized() { |
| 75 return sIsInitialized; |
| 76 } |
| 77 |
| 78 @VisibleForTesting |
| 79 public static void setProviderAuthorityForTests(String providerAuthority) { |
| 80 sProviderAuthority = providerAuthority; |
| 81 } |
| 82 |
| 83 /** |
| 84 * For security, we only allow system package to be a browser customizations
provider. However, |
| 85 * requiring root and installing system apk makes testing harder, so we deci
ded to have this |
| 86 * hack for testing. This must not be called other than tests. |
| 87 * |
| 88 * @param ignore whether we should ignore browser provider system package ch
ecking. |
| 89 */ |
| 90 @VisibleForTesting |
| 91 public static void ignoreBrowserProviderSystemPackageCheckForTests(boolean i
gnore) { |
| 92 sIgnoreBrowserProviderSystemPackageCheck = ignore; |
| 93 } |
| 94 |
| 95 @VisibleForTesting |
| 96 public static Uri buildQueryUri(String path) { |
| 97 return new Uri.Builder() |
| 98 .scheme("content") |
| 99 .authority(sProviderAuthority) |
| 100 .appendPath(path) |
| 101 .build(); |
| 102 } |
| 103 |
| 104 /** |
| 105 * Constructs an async task that reads PartnerBrowserCustomization provider. |
| 106 * |
| 107 * @param context The current application context. |
| 108 * @param timeoutMs If initializing takes more than this time, cancels it. T
he unit is ms. |
| 109 */ |
| 110 public static void initializeAsync(final Context context, long timeoutMs) { |
| 111 sIsInitialized = false; |
| 112 // Setup an initializing async task. |
| 113 final AsyncTask<Void, Void, Void> initializeAsyncTask = |
| 114 new AsyncTask<Void, Void, Void>() { |
| 115 private boolean mDisablePartnerBookmarksShim; |
| 116 private boolean mHomepageUriChanged; |
| 117 |
| 118 private void refreshHomepage() { |
| 119 try { |
| 120 ContentResolver contentResolver = context.getContentResolver
(); |
| 121 Cursor cursor = contentResolver.query( |
| 122 buildQueryUri(PARTNER_HOMEPAGE_PATH), null, null, nu
ll, null); |
| 123 if (cursor != null && cursor.moveToFirst() && cursor.getColu
mnCount() == 1 |
| 124 && !isCancelled()) { |
| 125 if (TextUtils.isEmpty(sHomepage) |
| 126 || !sHomepage.equals(cursor.getString(0))) { |
| 127 mHomepageUriChanged = true; |
| 128 } |
| 129 sHomepage = cursor.getString(0); |
| 130 } |
| 131 if (cursor != null) cursor.close(); |
| 132 } catch (Exception e) { |
| 133 Log.w(TAG, "Partner homepage provider URL read failed : ", e
); |
| 134 } |
| 135 } |
| 136 |
| 137 private void refreshIncognitoModeDisabled() { |
| 138 try { |
| 139 ContentResolver contentResolver = context.getContentResolver
(); |
| 140 Cursor cursor = contentResolver.query( |
| 141 buildQueryUri(PARTNER_DISABLE_INCOGNITO_MODE_PATH), |
| 142 null, null, null, null); |
| 143 if (cursor != null && cursor.moveToFirst() && cursor.getColu
mnCount() == 1 |
| 144 && !isCancelled()) { |
| 145 sIncognitoModeDisabled = cursor.getInt(0) == 1; |
| 146 } |
| 147 if (cursor != null) cursor.close(); |
| 148 } catch (Exception e) { |
| 149 Log.w(TAG, "Partner disable incognito mode read failed : ",
e); |
| 150 } |
| 151 } |
| 152 |
| 153 private void refreshBookmarksEditingDisabled() { |
| 154 try { |
| 155 ContentResolver contentResolver = context.getContentResolver
(); |
| 156 Cursor cursor = contentResolver.query( |
| 157 buildQueryUri(PARTNER_DISABLE_BOOKMARKS_EDITING_PATH
), |
| 158 null, null, null, null); |
| 159 if (cursor != null && cursor.moveToFirst() && cursor.getColu
mnCount() == 1 |
| 160 && !isCancelled()) { |
| 161 boolean bookmarksEditingDisabled = cursor.getInt(0) == 1
; |
| 162 if (bookmarksEditingDisabled != sBookmarksEditingDisable
d) { |
| 163 mDisablePartnerBookmarksShim = true; |
| 164 } |
| 165 sBookmarksEditingDisabled = bookmarksEditingDisabled; |
| 166 } |
| 167 if (cursor != null) cursor.close(); |
| 168 } catch (Exception e) { |
| 169 Log.w(TAG, "Partner disable bookmarks editing read failed :
", e); |
| 170 } |
| 171 } |
| 172 |
| 173 @Override |
| 174 protected Void doInBackground(Void... params) { |
| 175 try { |
| 176 ProviderInfo providerInfo = context.getPackageManager() |
| 177 .resolveContentProvider(sProviderAuthority, 0); |
| 178 if (providerInfo == null) return null; |
| 179 |
| 180 if ((providerInfo.applicationInfo.flags & ApplicationInfo.FL
AG_SYSTEM) == 0 |
| 181 && !sIgnoreBrowserProviderSystemPackageCheck) { |
| 182 Log.w("TAG", "Browser Cutomizations content provider pac
kage, " |
| 183 + providerInfo.packageName + ", is not a system
package. " |
| 184 + "This could be a malicious attepment from a th
ird party app, " |
| 185 + "so skip reading the browser content provider.
"); |
| 186 return null; |
| 187 } |
| 188 |
| 189 if (isCancelled()) return null; |
| 190 refreshIncognitoModeDisabled(); |
| 191 |
| 192 if (isCancelled()) return null; |
| 193 refreshBookmarksEditingDisabled(); |
| 194 |
| 195 if (isCancelled()) return null; |
| 196 refreshHomepage(); |
| 197 } catch (Exception e) { |
| 198 Log.w(TAG, "Fetching partner customizations failed", e); |
| 199 } |
| 200 return null; |
| 201 } |
| 202 |
| 203 @Override |
| 204 protected void onPostExecute(Void result) { |
| 205 onFinalized(); |
| 206 } |
| 207 |
| 208 @Override |
| 209 protected void onCancelled(Void result) { |
| 210 onFinalized(); |
| 211 } |
| 212 |
| 213 private void onFinalized() { |
| 214 sIsInitialized = true; |
| 215 |
| 216 for (Runnable callback : sInitializeAsyncCallbacks) { |
| 217 callback.run(); |
| 218 } |
| 219 sInitializeAsyncCallbacks.clear(); |
| 220 |
| 221 if (mHomepageUriChanged) { |
| 222 HomepageManager.getInstance(context).notifyHomepageUpdated()
; |
| 223 } |
| 224 |
| 225 // Disable partner bookmarks editing if necessary. |
| 226 if (mDisablePartnerBookmarksShim) { |
| 227 PartnerBookmarksReader.disablePartnerBookmarksEditing(); |
| 228 } |
| 229 } |
| 230 }; |
| 231 |
| 232 initializeAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
| 233 |
| 234 // Cancel the initialization if it reaches timeout. |
| 235 ThreadUtils.postOnUiThreadDelayed(new Runnable() { |
| 236 @Override |
| 237 public void run() { |
| 238 initializeAsyncTask.cancel(true); |
| 239 } |
| 240 }, timeoutMs); |
| 241 } |
| 242 |
| 243 /** |
| 244 * Sets a callback that will be executed when the initialization is done. |
| 245 * |
| 246 * @param callback This is called when the initialization is done. |
| 247 */ |
| 248 public static void setOnInitializeAsyncFinished(final Runnable callback) { |
| 249 if (sIsInitialized) { |
| 250 ThreadUtils.postOnUiThread(callback); |
| 251 } else { |
| 252 sInitializeAsyncCallbacks.add(callback); |
| 253 } |
| 254 } |
| 255 |
| 256 /** |
| 257 * Sets a callback that will be executed when the initialization is done. |
| 258 * |
| 259 * @param callback This is called when the initialization is done. |
| 260 * @param timeoutMs If initializing takes more than this time since this fun
ction is called, |
| 261 * force run |callback| early. The unit is ms. |
| 262 */ |
| 263 public static void setOnInitializeAsyncFinished(final Runnable callback, lon
g timeoutMs) { |
| 264 sInitializeAsyncCallbacks.add(callback); |
| 265 |
| 266 ThreadUtils.postOnUiThreadDelayed( |
| 267 new Runnable() { |
| 268 @Override |
| 269 public void run() { |
| 270 if (sInitializeAsyncCallbacks.remove(callback)) callback
.run(); |
| 271 } |
| 272 }, |
| 273 sIsInitialized ? 0 : timeoutMs); |
| 274 } |
| 275 |
| 276 public static void destroy() { |
| 277 sIsInitialized = false; |
| 278 sInitializeAsyncCallbacks.clear(); |
| 279 sHomepage = null; |
| 280 } |
| 281 |
| 282 /** |
| 283 * @return Home page URL from Android provider. If null, that means either t
here is no homepage |
| 284 * provider or provider set it to null to disable homepage. |
| 285 */ |
| 286 public static String getHomePageUrl() { |
| 287 return sHomepage; |
| 288 } |
| 289 } |
OLD | NEW |