Index: chrome/android/java/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProvider.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProvider.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3baf141b3cade70f0861ff46dacc826b6fa5df4c |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProvider.java |
@@ -0,0 +1,180 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser.superviseduser; |
+ |
+import android.os.Bundle; |
+import android.util.Pair; |
+ |
+import org.chromium.base.ThreadUtils; |
+import org.chromium.base.VisibleForTesting; |
+import org.chromium.base.annotations.CalledByNative; |
+import org.chromium.base.library_loader.LibraryProcessType; |
+import org.chromium.base.library_loader.ProcessInitException; |
+import org.chromium.components.webrestriction.WebRestrictionsContentProvider; |
+import org.chromium.content.browser.BrowserStartupController; |
+ |
+import java.util.concurrent.CountDownLatch; |
+ |
+/** |
+ * Content provider for telling other apps (e.g. WebView apps) about the supervised user URL filter. |
+ */ |
+public class SupervisedUserContentProvider extends WebRestrictionsContentProvider { |
+ private long mNativeSupervisedUserContentProvider = 0; |
+ |
+ private long getSupervisedUserContentProvider() throws ProcessInitException { |
+ if (mNativeSupervisedUserContentProvider != 0) { |
+ return mNativeSupervisedUserContentProvider; |
+ } |
+ |
+ BrowserStartupController.get(getContext(), LibraryProcessType.PROCESS_BROWSER) |
+ .startBrowserProcessesSync(false); |
+ |
+ mNativeSupervisedUserContentProvider = nativeCreateSupervisedUserContentProvider(); |
+ return mNativeSupervisedUserContentProvider; |
+ } |
+ |
+ @VisibleForTesting |
+ void setNativeSupervisedUserContentProviderForTesting(long nativeProvider) { |
+ mNativeSupervisedUserContentProvider = nativeProvider; |
+ } |
+ |
+ @VisibleForTesting |
+ static class SupervisedUserQueryReply { |
+ final CountDownLatch mLatch = new CountDownLatch(1); |
+ private Pair<Boolean, String> mResult; |
+ @VisibleForTesting |
+ @CalledByNative("SupervisedUserQueryReply") |
+ void onQueryComplete(boolean result, String errorMessage) { |
+ // This must be called precisely once per query. |
+ assert mResult == null; |
+ mResult = new Pair<Boolean, String>(result, errorMessage); |
+ mLatch.countDown(); |
+ } |
+ Pair<Boolean, String> getResult() throws InterruptedException { |
+ mLatch.await(); |
+ return mResult; |
+ } |
+ } |
+ |
+ @Override |
+ protected Pair<Boolean, String> shouldProceed(final String url) { |
+ // This will be called on multiple threads (but never the UI thread), |
+ // see http://developer.android.com/guide/components/processes-and-threads.html#ThreadSafe. |
+ // The reply comes back on a different thread (possibly the UI thread) some time later. |
+ // As such it needs to correctly match the replies to the calls. It does this by creating a |
+ // reply object for each query, and passing this through the callback structure. The reply |
+ // object also handles waiting for the reply. |
+ assert !ThreadUtils.runningOnUiThread(); |
+ final SupervisedUserQueryReply queryReply = new SupervisedUserQueryReply(); |
+ ThreadUtils.runOnUiThread(new Runnable() { |
+ @Override |
+ public void run() { |
+ try { |
+ nativeShouldProceed(getSupervisedUserContentProvider(), queryReply, url); |
+ } catch (ProcessInitException e) { |
+ queryReply.onQueryComplete(false, null); |
+ } |
+ } |
+ }); |
+ try { |
+ // This will block until an onQueryComplete call on a different thread adds |
+ // something to the queue. |
+ return queryReply.getResult(); |
+ } catch (InterruptedException e) { |
+ return new Pair<Boolean, String>(false, null); |
+ } |
+ } |
+ |
+ @Override |
+ protected boolean canInsert() { |
+ // Chrome always allows insertion requests. |
+ return true; |
+ } |
+ |
+ @VisibleForTesting |
+ static class SupervisedUserInsertReply { |
+ final CountDownLatch mLatch = new CountDownLatch(1); |
+ boolean mResult; |
+ @VisibleForTesting |
+ @CalledByNative("SupervisedUserInsertReply") |
+ void onInsertRequestSendComplete(boolean result) { |
+ // This must be called precisely once per query. |
+ assert mLatch.getCount() == 1; |
+ mResult = result; |
+ mLatch.countDown(); |
+ } |
+ boolean getResult() throws InterruptedException { |
+ mLatch.await(); |
+ return mResult; |
+ } |
+ } |
+ |
+ @Override |
+ protected boolean requestInsert(final String url) { |
+ // This will be called on multiple threads (but never the UI thread), |
+ // see http://developer.android.com/guide/components/processes-and-threads.html#ThreadSafe. |
+ // The reply comes back on a different thread (possibly the UI thread) some time later. |
+ // As such it needs to correctly match the replies to the calls. It does this by creating a |
+ // reply object for each query, and passing this through the callback structure. The reply |
+ // object also handles waiting for the reply. |
+ assert !ThreadUtils.runningOnUiThread(); |
+ final SupervisedUserInsertReply insertReply = new SupervisedUserInsertReply(); |
+ ThreadUtils.runOnUiThread(new Runnable() { |
+ @Override |
+ public void run() { |
+ try { |
+ nativeRequestInsert(getSupervisedUserContentProvider(), insertReply, url); |
+ } catch (ProcessInitException e) { |
+ insertReply.onInsertRequestSendComplete(false); |
+ } |
+ } |
+ }); |
+ try { |
+ return insertReply.getResult(); |
+ } catch (InterruptedException e) { |
+ return false; |
+ } |
+ } |
+ |
+ @VisibleForTesting |
+ @Override |
+ public Bundle call(String method, String arg, Bundle bundle) { |
+ if (method.equals("setFilterForTesting")) setFilterForTesting(); |
+ return null; |
+ } |
+ |
+ @VisibleForTesting |
+ void setFilterForTesting() { |
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
+ @Override |
+ public void run() { |
+ try { |
+ nativeSetFilterForTesting(getSupervisedUserContentProvider()); |
+ } catch (ProcessInitException e) { |
+ // There is no way of returning anything sensible here, so ignore the error and |
+ // do nothing. |
+ } |
+ } |
+ }); |
+ } |
+ |
+ @VisibleForTesting |
+ @CalledByNative |
+ void onSupervisedUserFilterUpdated() { |
+ onFilterChanged(); |
+ } |
+ |
+ @VisibleForTesting native long nativeCreateSupervisedUserContentProvider(); |
+ |
+ @VisibleForTesting |
+ native void nativeShouldProceed(long nativeSupervisedUserContentProvider, |
+ SupervisedUserQueryReply queryReply, String url); |
+ |
+ @VisibleForTesting |
+ native void nativeRequestInsert(long nativeSupervisedUserContentProvider, |
+ SupervisedUserInsertReply insertReply, String url); |
+ |
+ private native void nativeSetFilterForTesting(long nativeSupervisedUserContentProvider); |
+} |