Chromium Code Reviews| 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(); |
|
Bernhard Bauer
2016/01/05 10:22:32
Actually... if everything but this works without u
aberent
2016/01/05 10:48:38
On further thought this assertion no longer makes
|
| + 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); |
| +} |