| 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 | 
| index 04f5e112ba82a5857377d6871df69ea80f56b021..88bd0ea071e86442d5127e0046fa7e6618d9c795 100644 | 
| --- a/chrome/android/java/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProvider.java | 
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProvider.java | 
| @@ -14,13 +14,18 @@ import android.os.Bundle; | 
| import android.os.SystemClock; | 
| import android.os.UserManager; | 
|  | 
| +import org.chromium.base.Callback; | 
| import org.chromium.base.ThreadUtils; | 
| import org.chromium.base.VisibleForTesting; | 
| import org.chromium.base.annotations.CalledByNative; | 
| import org.chromium.base.library_loader.LibraryLoader; | 
| import org.chromium.base.library_loader.ProcessInitException; | 
| import org.chromium.base.metrics.RecordHistogram; | 
| +import org.chromium.chrome.browser.childaccounts.ChildAccountService; | 
| +import org.chromium.chrome.browser.firstrun.ForcedSigninProcessor; | 
| import org.chromium.chrome.browser.init.ChromeBrowserInitializer; | 
| +import org.chromium.components.signin.ChromeSigninController; | 
| +import org.chromium.components.supervisedusererrorpage.FilteringBehaviorReason; | 
| import org.chromium.components.webrestrictions.browser.WebRestrictionsContentProvider; | 
|  | 
| import java.util.concurrent.ArrayBlockingQueue; | 
| @@ -36,24 +41,78 @@ public class SupervisedUserContentProvider extends WebRestrictionsContentProvide | 
| private long mNativeSupervisedUserContentProvider; | 
| private boolean mChromeAlreadyStarted; | 
| private static Object sEnabledLock = new Object(); | 
| +    private static Object sContentProviderLock = new Object(); | 
| + | 
| +    private static final String TAG = "SupervisedUserContent"; | 
|  | 
| // Three value "boolean" caching enabled state, null if not yet known. | 
| private static Boolean sEnabled; | 
|  | 
| -    private long getSupervisedUserContentProvider() throws ProcessInitException { | 
| -        mChromeAlreadyStarted = LibraryLoader.isInitialized(); | 
| -        if (mNativeSupervisedUserContentProvider != 0) { | 
| -            return mNativeSupervisedUserContentProvider; | 
| -        } | 
| - | 
| -        ChromeBrowserInitializer.getInstance(getContext()).handleSynchronousStartup(); | 
| +    @VisibleForTesting | 
| +    void startForcedSigninProcessor(Context appContext, Runnable onComplete) { | 
| +        ForcedSigninProcessor.start(appContext, onComplete); | 
| +    } | 
|  | 
| -        mNativeSupervisedUserContentProvider = nativeCreateSupervisedUserContentProvider(); | 
| -        return mNativeSupervisedUserContentProvider; | 
| +    @VisibleForTesting | 
| +    void listenForChildAccountStatusChange(Callback<Boolean> callback) { | 
| +        ChildAccountService.listenForStatusChange(callback); | 
| } | 
|  | 
| -    void setNativeSupervisedUserContentProviderForTesting(long nativeProvider) { | 
| -        mNativeSupervisedUserContentProvider = nativeProvider; | 
| +    private long getSupervisedUserContentProvider() { | 
| +        // This may lock for some time, but is always called on a background thread, and will only | 
| +        // take significant time if the Chrome process isn't already running. | 
| +        synchronized (sContentProviderLock) { | 
| +            mChromeAlreadyStarted = LibraryLoader.isInitialized(); | 
| +            if (mNativeSupervisedUserContentProvider != 0) { | 
| +                return mNativeSupervisedUserContentProvider; | 
| +            } | 
| +            final Context appContext = getContext().getApplicationContext(); | 
| +            final SupervisedUserReply<Long> reply = new SupervisedUserReply<>(); | 
| +            ThreadUtils.runOnUiThread(new Runnable() { | 
| +                @Override | 
| +                public void run() { | 
| +                    try { | 
| +                        ChromeBrowserInitializer.getInstance(appContext).handleSynchronousStartup(); | 
| +                    } catch (ProcessInitException e) { | 
| +                        reply.onQueryFinished(0L); | 
| +                        return; | 
| +                    } | 
| +                    final ChromeSigninController chromeSigninController = | 
| +                            ChromeSigninController.get(appContext); | 
| +                    if (chromeSigninController.isSignedIn()) { | 
| +                        reply.onQueryFinished(nativeCreateSupervisedUserContentProvider()); | 
| +                        return; | 
| +                    } | 
| +                    // Try to sign in, Chrome needs to be signed in to get the URL filter. | 
| +                    startForcedSigninProcessor(appContext, new Runnable() { | 
| +                        @Override | 
| +                        public void run() { | 
| +                            if (!chromeSigninController.isSignedIn()) { | 
| +                                reply.onQueryFinished(0L); | 
| +                                return; | 
| +                            } | 
| +                            // Wait for the status change; Chrome can't check any URLs until this | 
| +                            // has happened. | 
| +                            listenForChildAccountStatusChange(new Callback<Boolean>() { | 
| +                                @Override | 
| +                                public void onResult(Boolean result) { | 
| +                                    reply.onQueryFinished( | 
| +                                            nativeCreateSupervisedUserContentProvider()); | 
| +                                } | 
| +                            }); | 
| +                        } | 
| +                    }); | 
| +                } | 
| +            }); | 
| +            try { | 
| +                Long result = reply.getResult(); | 
| +                if (result == null) return 0; | 
| +                mNativeSupervisedUserContentProvider = result; | 
| +                return mNativeSupervisedUserContentProvider; | 
| +            } catch (InterruptedException e) { | 
| +                return 0; | 
| +            } | 
| +        } | 
| } | 
|  | 
| static class SupervisedUserReply<T> { | 
| @@ -109,14 +168,15 @@ public class SupervisedUserContentProvider extends WebRestrictionsContentProvide | 
| // object also handles waiting for the reply. | 
| long startTimeMs = SystemClock.elapsedRealtime(); | 
| final SupervisedUserQueryReply queryReply = new SupervisedUserQueryReply(); | 
| +        final long contentProvider = getSupervisedUserContentProvider(); | 
| +        if (contentProvider == 0) { | 
| +            return new WebRestrictionsResult( | 
| +                    false, new int[] {FilteringBehaviorReason.NOT_SIGNED_IN}, null); | 
| +        } | 
| ThreadUtils.runOnUiThread(new Runnable() { | 
| @Override | 
| public void run() { | 
| -                try { | 
| -                    nativeShouldProceed(getSupervisedUserContentProvider(), queryReply, url); | 
| -                } catch (ProcessInitException e) { | 
| -                    queryReply.onQueryFailedNoErrorData(); | 
| -                } | 
| +                nativeShouldProceed(contentProvider, queryReply, url); | 
| } | 
| }); | 
| try { | 
| @@ -159,14 +219,12 @@ public class SupervisedUserContentProvider extends WebRestrictionsContentProvide | 
| // reply object for each query, and passing this through the callback structure. The reply | 
| // object also handles waiting for the reply. | 
| final SupervisedUserInsertReply insertReply = new SupervisedUserInsertReply(); | 
| +        final long contentProvider = getSupervisedUserContentProvider(); | 
| +        if (contentProvider == 0) return false; | 
| ThreadUtils.runOnUiThread(new Runnable() { | 
| @Override | 
| public void run() { | 
| -                try { | 
| -                    nativeRequestInsert(getSupervisedUserContentProvider(), insertReply, url); | 
| -                } catch (ProcessInitException e) { | 
| -                    insertReply.onInsertRequestSendComplete(false); | 
| -                } | 
| +                nativeRequestInsert(contentProvider, insertReply, url); | 
| } | 
| }); | 
| try { | 
| @@ -178,26 +236,6 @@ public class SupervisedUserContentProvider extends WebRestrictionsContentProvide | 
| } | 
| } | 
|  | 
| -    @Override | 
| -    public Bundle call(String method, String arg, Bundle bundle) { | 
| -        if (method.equals("setFilterForTesting")) setFilterForTesting(); | 
| -        return null; | 
| -    } | 
| - | 
| -    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. | 
| -                } | 
| -            } | 
| -        }); | 
| -    } | 
| - | 
| @CalledByNative | 
| void onSupervisedUserFilterUpdated() { | 
| onFilterChanged(); | 
| @@ -249,7 +287,38 @@ public class SupervisedUserContentProvider extends WebRestrictionsContentProvide | 
| Bundle appRestrictions = userManager | 
| .getApplicationRestrictions(getContext().getPackageName()); | 
| setEnabled(appRestrictions.getBoolean(SUPERVISED_USER_CONTENT_PROVIDER_ENABLED)); | 
| -    }; | 
| +    } | 
| + | 
| +    @Override | 
| +    protected String[] getErrorColumnNames() { | 
| +        String result[] = {"Reason", "Allow access requests", "Is child account", | 
| +                "Profile image URL", "Second profile image URL", "Custodian", "Custodian email", | 
| +                "Second custodian", "Second custodian email"}; | 
| +        return result; | 
| +    } | 
| + | 
| +    // Helpers for testing. | 
| + | 
| +    @Override | 
| +    public Bundle call(String method, String arg, Bundle bundle) { | 
| +        if (method.equals("setFilterForTesting")) setFilterForTesting(); | 
| +        return null; | 
| +    } | 
| + | 
| +    void setFilterForTesting() { | 
| +        final long contentProvider = getSupervisedUserContentProvider(); | 
| +        if (contentProvider == 0) return; | 
| +        ThreadUtils.runOnUiThread(new Runnable() { | 
| +            @Override | 
| +            public void run() { | 
| +                nativeSetFilterForTesting(contentProvider); | 
| +            } | 
| +        }); | 
| +    } | 
| + | 
| +    void setNativeSupervisedUserContentProviderForTesting(long nativeProvider) { | 
| +        mNativeSupervisedUserContentProvider = nativeProvider; | 
| +    } | 
|  | 
| @VisibleForTesting | 
| public static void enableContentProviderForTesting() { | 
| @@ -266,11 +335,4 @@ public class SupervisedUserContentProvider extends WebRestrictionsContentProvide | 
|  | 
| private native void nativeSetFilterForTesting(long nativeSupervisedUserContentProvider); | 
|  | 
| -    @Override | 
| -    protected String[] getErrorColumnNames() { | 
| -        String result[] = {"Reason", "Allow access requests", "Is child account", | 
| -                "Profile image URL", "Second profile image URL", "Custodian", "Custodian email", | 
| -                "Second custodian", "Second custodian email"}; | 
| -        return result; | 
| -    } | 
| } | 
|  |