Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chrome.browser.superviseduser; | 5 package org.chromium.chrome.browser.superviseduser; |
| 6 | 6 |
| 7 import android.annotation.TargetApi; | 7 import android.annotation.TargetApi; |
| 8 import android.content.BroadcastReceiver; | 8 import android.content.BroadcastReceiver; |
| 9 import android.content.Context; | 9 import android.content.Context; |
| 10 import android.content.Intent; | 10 import android.content.Intent; |
| 11 import android.content.IntentFilter; | 11 import android.content.IntentFilter; |
| 12 import android.os.Build; | 12 import android.os.Build; |
| 13 import android.os.Bundle; | 13 import android.os.Bundle; |
| 14 import android.os.SystemClock; | 14 import android.os.SystemClock; |
| 15 import android.os.UserManager; | 15 import android.os.UserManager; |
| 16 | 16 |
| 17 import org.chromium.base.ThreadUtils; | 17 import org.chromium.base.ThreadUtils; |
| 18 import org.chromium.base.VisibleForTesting; | 18 import org.chromium.base.VisibleForTesting; |
| 19 import org.chromium.base.annotations.CalledByNative; | 19 import org.chromium.base.annotations.CalledByNative; |
| 20 import org.chromium.base.library_loader.LibraryLoader; | 20 import org.chromium.base.library_loader.LibraryLoader; |
| 21 import org.chromium.base.library_loader.ProcessInitException; | 21 import org.chromium.base.library_loader.ProcessInitException; |
| 22 import org.chromium.base.metrics.RecordHistogram; | 22 import org.chromium.base.metrics.RecordHistogram; |
| 23 import org.chromium.chrome.browser.childaccounts.ChildAccountService; | |
| 24 import org.chromium.chrome.browser.firstrun.ForcedSigninProcessor; | |
| 23 import org.chromium.chrome.browser.init.ChromeBrowserInitializer; | 25 import org.chromium.chrome.browser.init.ChromeBrowserInitializer; |
| 26 import org.chromium.components.signin.ChromeSigninController; | |
| 24 import org.chromium.components.webrestrictions.browser.WebRestrictionsContentPro vider; | 27 import org.chromium.components.webrestrictions.browser.WebRestrictionsContentPro vider; |
| 25 | 28 |
| 26 import java.util.concurrent.ArrayBlockingQueue; | 29 import java.util.concurrent.ArrayBlockingQueue; |
| 27 import java.util.concurrent.BlockingQueue; | 30 import java.util.concurrent.BlockingQueue; |
| 28 import java.util.concurrent.TimeUnit; | 31 import java.util.concurrent.TimeUnit; |
| 29 | 32 |
| 30 /** | 33 /** |
| 31 * Content provider for telling other apps (e.g. WebView apps) about the supervi sed user URL filter. | 34 * Content provider for telling other apps (e.g. WebView apps) about the supervi sed user URL filter. |
| 32 */ | 35 */ |
| 33 public class SupervisedUserContentProvider extends WebRestrictionsContentProvide r { | 36 public class SupervisedUserContentProvider extends WebRestrictionsContentProvide r { |
| 34 private static final String SUPERVISED_USER_CONTENT_PROVIDER_ENABLED = | 37 private static final String SUPERVISED_USER_CONTENT_PROVIDER_ENABLED = |
| 35 "SupervisedUserContentProviderEnabled"; | 38 "SupervisedUserContentProviderEnabled"; |
| 36 private long mNativeSupervisedUserContentProvider; | 39 private long mNativeSupervisedUserContentProvider; |
| 37 private boolean mChromeAlreadyStarted; | 40 private boolean mChromeAlreadyStarted; |
| 38 private static Object sEnabledLock = new Object(); | 41 private static Object sEnabledLock = new Object(); |
| 42 private static Object sContentProviderLock = new Object(); | |
| 43 | |
| 44 // Must match the value in supervised_user_error_page.h | |
| 45 private static final int NOT_SIGNED_IN = 5; | |
| 46 | |
| 47 private static final String TAG = "SupervisedUserContent"; | |
| 39 | 48 |
| 40 // Three value "boolean" caching enabled state, null if not yet known. | 49 // Three value "boolean" caching enabled state, null if not yet known. |
| 41 private static Boolean sEnabled; | 50 private static Boolean sEnabled; |
| 42 | 51 |
| 43 private long getSupervisedUserContentProvider() throws ProcessInitException { | 52 @VisibleForTesting |
| 44 mChromeAlreadyStarted = LibraryLoader.isInitialized(); | 53 void startForcedSigninProcessor(Context appContext, Runnable onComplete) { |
| 45 if (mNativeSupervisedUserContentProvider != 0) { | 54 ForcedSigninProcessor.start(appContext, onComplete); |
| 46 return mNativeSupervisedUserContentProvider; | |
| 47 } | |
| 48 | |
| 49 ChromeBrowserInitializer.getInstance(getContext()).handleSynchronousStar tup(); | |
| 50 | |
| 51 mNativeSupervisedUserContentProvider = nativeCreateSupervisedUserContent Provider(); | |
| 52 return mNativeSupervisedUserContentProvider; | |
| 53 } | 55 } |
| 54 | 56 |
| 55 void setNativeSupervisedUserContentProviderForTesting(long nativeProvider) { | 57 @VisibleForTesting |
| 56 mNativeSupervisedUserContentProvider = nativeProvider; | 58 void listenForChildAccountStatusChange(Runnable callback) { |
| 59 ChildAccountService.listenForStatusChange(callback); | |
| 60 } | |
| 61 | |
| 62 private long getSupervisedUserContentProvider() { | |
| 63 synchronized (sContentProviderLock) { | |
|
Bernhard Bauer
2017/02/15 15:09:37
Maybe add a comment that this potentially holds th
aberent
2017/02/15 18:34:46
Done.
| |
| 64 mChromeAlreadyStarted = LibraryLoader.isInitialized(); | |
| 65 if (mNativeSupervisedUserContentProvider != 0) { | |
| 66 return mNativeSupervisedUserContentProvider; | |
| 67 } | |
| 68 final Context appContext = getContext().getApplicationContext(); | |
| 69 final SupervisedUserReply<Long> reply = new SupervisedUserReply<>(); | |
| 70 ThreadUtils.runOnUiThread(new Runnable() { | |
| 71 @Override | |
| 72 public void run() { | |
| 73 try { | |
| 74 ChromeBrowserInitializer.getInstance(appContext).handleS ynchronousStartup(); | |
| 75 } catch (ProcessInitException e) { | |
| 76 reply.onQueryFinished(0L); | |
| 77 return; | |
| 78 } | |
| 79 final ChromeSigninController chromeSigninController = | |
| 80 ChromeSigninController.get(appContext); | |
| 81 if (chromeSigninController.isSignedIn()) { | |
| 82 reply.onQueryFinished(nativeCreateSupervisedUserContentP rovider()); | |
| 83 return; | |
| 84 } | |
| 85 // Try to sign in, Chrome needs to be signed in to get the U RL filter. | |
| 86 startForcedSigninProcessor(appContext, new Runnable() { | |
| 87 @Override | |
| 88 public void run() { | |
| 89 if (!chromeSigninController.isSignedIn()) { | |
| 90 reply.onQueryFinished(0L); | |
| 91 return; | |
| 92 } | |
| 93 // Wait for the status change; Chrome can't check an y URLs until this | |
| 94 // has happened. | |
| 95 listenForChildAccountStatusChange(new Runnable() { | |
| 96 @Override | |
| 97 public void run() { | |
| 98 reply.onQueryFinished( | |
| 99 nativeCreateSupervisedUserContentPro vider()); | |
| 100 } | |
| 101 }); | |
| 102 } | |
| 103 }); | |
| 104 } | |
| 105 }); | |
| 106 try { | |
| 107 mNativeSupervisedUserContentProvider = reply.getResult(); | |
| 108 return mNativeSupervisedUserContentProvider; | |
| 109 } catch (InterruptedException e) { | |
| 110 return 0; | |
| 111 } | |
| 112 } | |
| 57 } | 113 } |
| 58 | 114 |
| 59 static class SupervisedUserReply<T> { | 115 static class SupervisedUserReply<T> { |
| 60 private static final long RESULT_TIMEOUT_SECONDS = 10; | 116 private static final long RESULT_TIMEOUT_SECONDS = 10; |
| 61 final BlockingQueue<T> mQueue = new ArrayBlockingQueue<>(1); | 117 final BlockingQueue<T> mQueue = new ArrayBlockingQueue<>(1); |
| 62 | 118 |
| 63 void onQueryFinished(T reply) { | 119 void onQueryFinished(T reply) { |
| 64 // This must be called precisely once per query. | 120 // This must be called precisely once per query. |
| 65 mQueue.add(reply); | 121 mQueue.add(reply); |
| 66 } | 122 } |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 102 @Override | 158 @Override |
| 103 protected WebRestrictionsResult shouldProceed(final String url) { | 159 protected WebRestrictionsResult shouldProceed(final String url) { |
| 104 // This will be called on multiple threads (but never the UI thread), | 160 // This will be called on multiple threads (but never the UI thread), |
| 105 // see http://developer.android.com/guide/components/processes-and-threa ds.html#ThreadSafe. | 161 // see http://developer.android.com/guide/components/processes-and-threa ds.html#ThreadSafe. |
| 106 // The reply comes back on a different thread (possibly the UI thread) s ome time later. | 162 // The reply comes back on a different thread (possibly the UI thread) s ome time later. |
| 107 // As such it needs to correctly match the replies to the calls. It does this by creating a | 163 // As such it needs to correctly match the replies to the calls. It does this by creating a |
| 108 // reply object for each query, and passing this through the callback st ructure. The reply | 164 // reply object for each query, and passing this through the callback st ructure. The reply |
| 109 // object also handles waiting for the reply. | 165 // object also handles waiting for the reply. |
| 110 long startTimeMs = SystemClock.elapsedRealtime(); | 166 long startTimeMs = SystemClock.elapsedRealtime(); |
| 111 final SupervisedUserQueryReply queryReply = new SupervisedUserQueryReply (); | 167 final SupervisedUserQueryReply queryReply = new SupervisedUserQueryReply (); |
| 168 final long contentProvider = getSupervisedUserContentProvider(); | |
| 169 if (contentProvider == 0) { | |
| 170 return new WebRestrictionsResult(false, new int[] {NOT_SIGNED_IN}, n ull); | |
| 171 } | |
| 112 ThreadUtils.runOnUiThread(new Runnable() { | 172 ThreadUtils.runOnUiThread(new Runnable() { |
| 113 @Override | 173 @Override |
| 114 public void run() { | 174 public void run() { |
| 115 try { | 175 nativeShouldProceed(contentProvider, queryReply, url); |
| 116 nativeShouldProceed(getSupervisedUserContentProvider(), quer yReply, url); | |
| 117 } catch (ProcessInitException e) { | |
| 118 queryReply.onQueryFailedNoErrorData(); | |
| 119 } | |
| 120 } | 176 } |
| 121 }); | 177 }); |
| 122 try { | 178 try { |
| 123 // This will block until an onQueryComplete call on a different thre ad adds | 179 // This will block until an onQueryComplete call on a different thre ad adds |
| 124 // something to the queue. | 180 // something to the queue. |
| 125 WebRestrictionsResult result = queryReply.getResult(); | 181 WebRestrictionsResult result = queryReply.getResult(); |
| 126 String histogramName = mChromeAlreadyStarted | 182 String histogramName = mChromeAlreadyStarted |
| 127 ? "SupervisedUserContentProvider.ChromeStartedRequestTime" | 183 ? "SupervisedUserContentProvider.ChromeStartedRequestTime" |
| 128 : "SupervisedUserContentProvider.ChromeNotStartedRequestTime "; | 184 : "SupervisedUserContentProvider.ChromeNotStartedRequestTime "; |
| 129 RecordHistogram.recordTimesHistogram(histogramName, | 185 RecordHistogram.recordTimesHistogram(histogramName, |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 152 | 208 |
| 153 @Override | 209 @Override |
| 154 protected boolean requestInsert(final String url) { | 210 protected boolean requestInsert(final String url) { |
| 155 // This will be called on multiple threads (but never the UI thread), | 211 // This will be called on multiple threads (but never the UI thread), |
| 156 // see http://developer.android.com/guide/components/processes-and-threa ds.html#ThreadSafe. | 212 // see http://developer.android.com/guide/components/processes-and-threa ds.html#ThreadSafe. |
| 157 // The reply comes back on a different thread (possibly the UI thread) s ome time later. | 213 // The reply comes back on a different thread (possibly the UI thread) s ome time later. |
| 158 // As such it needs to correctly match the replies to the calls. It does this by creating a | 214 // As such it needs to correctly match the replies to the calls. It does this by creating a |
| 159 // reply object for each query, and passing this through the callback st ructure. The reply | 215 // reply object for each query, and passing this through the callback st ructure. The reply |
| 160 // object also handles waiting for the reply. | 216 // object also handles waiting for the reply. |
| 161 final SupervisedUserInsertReply insertReply = new SupervisedUserInsertRe ply(); | 217 final SupervisedUserInsertReply insertReply = new SupervisedUserInsertRe ply(); |
| 218 final long contentProvider = getSupervisedUserContentProvider(); | |
| 219 if (contentProvider == 0) return false; | |
| 162 ThreadUtils.runOnUiThread(new Runnable() { | 220 ThreadUtils.runOnUiThread(new Runnable() { |
| 163 @Override | 221 @Override |
| 164 public void run() { | 222 public void run() { |
| 165 try { | 223 nativeRequestInsert(contentProvider, insertReply, url); |
| 166 nativeRequestInsert(getSupervisedUserContentProvider(), inse rtReply, url); | |
| 167 } catch (ProcessInitException e) { | |
| 168 insertReply.onInsertRequestSendComplete(false); | |
| 169 } | |
| 170 } | 224 } |
| 171 }); | 225 }); |
| 172 try { | 226 try { |
| 173 Boolean result = insertReply.getResult(); | 227 Boolean result = insertReply.getResult(); |
| 174 if (result == null) return false; | 228 if (result == null) return false; |
| 175 return result; | 229 return result; |
| 176 } catch (InterruptedException e) { | 230 } catch (InterruptedException e) { |
| 177 return false; | 231 return false; |
| 178 } | 232 } |
| 179 } | 233 } |
| 180 | 234 |
| 181 @Override | |
| 182 public Bundle call(String method, String arg, Bundle bundle) { | |
| 183 if (method.equals("setFilterForTesting")) setFilterForTesting(); | |
| 184 return null; | |
| 185 } | |
| 186 | |
| 187 void setFilterForTesting() { | |
| 188 ThreadUtils.runOnUiThreadBlocking(new Runnable() { | |
| 189 @Override | |
| 190 public void run() { | |
| 191 try { | |
| 192 nativeSetFilterForTesting(getSupervisedUserContentProvider() ); | |
| 193 } catch (ProcessInitException e) { | |
| 194 // There is no way of returning anything sensible here, so i gnore the error and | |
| 195 // do nothing. | |
| 196 } | |
| 197 } | |
| 198 }); | |
| 199 } | |
| 200 | |
| 201 @CalledByNative | 235 @CalledByNative |
| 202 void onSupervisedUserFilterUpdated() { | 236 void onSupervisedUserFilterUpdated() { |
| 203 onFilterChanged(); | 237 onFilterChanged(); |
| 204 } | 238 } |
| 205 | 239 |
| 206 private static Boolean getEnabled() { | 240 private static Boolean getEnabled() { |
| 207 synchronized (sEnabledLock) { | 241 synchronized (sEnabledLock) { |
| 208 return sEnabled; | 242 return sEnabled; |
| 209 } | 243 } |
| 210 } | 244 } |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 242 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) | 276 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) |
| 243 private void updateEnabledState() { | 277 private void updateEnabledState() { |
| 244 // This method uses AppRestrictions directly, rather than using the Poli cy interface, | 278 // This method uses AppRestrictions directly, rather than using the Poli cy interface, |
| 245 // because it must be callable in contexts in which the native library h asn't been | 279 // because it must be callable in contexts in which the native library h asn't been |
| 246 // loaded. It will always be called from a background thread (except pos sibly in tests) | 280 // loaded. It will always be called from a background thread (except pos sibly in tests) |
| 247 // so can get the App Restrictions synchronously. | 281 // so can get the App Restrictions synchronously. |
| 248 UserManager userManager = (UserManager) getContext().getSystemService(Co ntext.USER_SERVICE); | 282 UserManager userManager = (UserManager) getContext().getSystemService(Co ntext.USER_SERVICE); |
| 249 Bundle appRestrictions = userManager | 283 Bundle appRestrictions = userManager |
| 250 .getApplicationRestrictions(getContext().getPackageName()); | 284 .getApplicationRestrictions(getContext().getPackageName()); |
| 251 setEnabled(appRestrictions.getBoolean(SUPERVISED_USER_CONTENT_PROVIDER_E NABLED)); | 285 setEnabled(appRestrictions.getBoolean(SUPERVISED_USER_CONTENT_PROVIDER_E NABLED)); |
| 252 }; | 286 } |
| 287 | |
| 288 @Override | |
| 289 protected String[] getErrorColumnNames() { | |
| 290 String result[] = {"Reason", "Allow access requests", "Is child account" , | |
| 291 "Profile image URL", "Second profile image URL", "Custodian", "C ustodian email", | |
| 292 "Second custodian", "Second custodian email"}; | |
| 293 return result; | |
| 294 } | |
| 295 | |
| 296 // Helpers for testing. | |
| 297 | |
| 298 @Override | |
| 299 public Bundle call(String method, String arg, Bundle bundle) { | |
| 300 if (method.equals("setFilterForTesting")) setFilterForTesting(); | |
| 301 return null; | |
| 302 } | |
| 303 | |
| 304 void setFilterForTesting() { | |
| 305 final long contentProvider = getSupervisedUserContentProvider(); | |
| 306 if (contentProvider == 0) return; | |
| 307 ThreadUtils.runOnUiThread(new Runnable() { | |
| 308 @Override | |
| 309 public void run() { | |
| 310 nativeSetFilterForTesting(contentProvider); | |
| 311 } | |
| 312 }); | |
| 313 } | |
| 314 | |
| 315 void setNativeSupervisedUserContentProviderForTesting(long nativeProvider) { | |
| 316 mNativeSupervisedUserContentProvider = nativeProvider; | |
| 317 } | |
| 253 | 318 |
| 254 @VisibleForTesting | 319 @VisibleForTesting |
| 255 public static void enableContentProviderForTesting() { | 320 public static void enableContentProviderForTesting() { |
| 256 setEnabled(true); | 321 setEnabled(true); |
| 257 } | 322 } |
| 258 | 323 |
| 259 native long nativeCreateSupervisedUserContentProvider(); | 324 native long nativeCreateSupervisedUserContentProvider(); |
| 260 | 325 |
| 261 native void nativeShouldProceed(long nativeSupervisedUserContentProvider, | 326 native void nativeShouldProceed(long nativeSupervisedUserContentProvider, |
| 262 SupervisedUserQueryReply queryReply, String url); | 327 SupervisedUserQueryReply queryReply, String url); |
| 263 | 328 |
| 264 native void nativeRequestInsert(long nativeSupervisedUserContentProvider, | 329 native void nativeRequestInsert(long nativeSupervisedUserContentProvider, |
| 265 SupervisedUserInsertReply insertReply, String url); | 330 SupervisedUserInsertReply insertReply, String url); |
| 266 | 331 |
| 267 private native void nativeSetFilterForTesting(long nativeSupervisedUserConte ntProvider); | 332 private native void nativeSetFilterForTesting(long nativeSupervisedUserConte ntProvider); |
| 268 | 333 |
| 269 @Override | |
| 270 protected String[] getErrorColumnNames() { | |
| 271 String result[] = {"Reason", "Allow access requests", "Is child account" , | |
| 272 "Profile image URL", "Second profile image URL", "Custodian", "C ustodian email", | |
| 273 "Second custodian", "Second custodian email"}; | |
| 274 return result; | |
| 275 } | |
| 276 } | 334 } |
| OLD | NEW |