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 |