Index: chrome/android/java/src/org/chromium/chrome/browser/child_accounts/ChildAccountService.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/child_accounts/ChildAccountService.java b/chrome/android/java/src/org/chromium/chrome/browser/child_accounts/ChildAccountService.java |
deleted file mode 100644 |
index 031649e82be871f0a5443c8fbe593be50897ad16..0000000000000000000000000000000000000000 |
--- a/chrome/android/java/src/org/chromium/chrome/browser/child_accounts/ChildAccountService.java |
+++ /dev/null |
@@ -1,361 +0,0 @@ |
-// Copyright 2014 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.child_accounts; |
- |
-import android.accounts.Account; |
-import android.accounts.AccountManager; |
-import android.accounts.AccountManagerCallback; |
-import android.accounts.AccountManagerFuture; |
-import android.accounts.AuthenticatorException; |
-import android.accounts.OnAccountsUpdateListener; |
-import android.accounts.OperationCanceledException; |
-import android.content.Context; |
-import android.os.AsyncTask; |
- |
-import org.chromium.base.CalledByNative; |
-import org.chromium.base.CommandLine; |
-import org.chromium.base.Log; |
-import org.chromium.base.ThreadUtils; |
-import org.chromium.base.TraceEvent; |
-import org.chromium.base.VisibleForTesting; |
-import org.chromium.chrome.browser.ChromeSwitches; |
-import org.chromium.chrome.browser.signin.SigninManager; |
-import org.chromium.sync.signin.AccountManagerHelper; |
- |
-import java.io.IOException; |
-import java.util.ArrayList; |
-import java.util.List; |
-import java.util.Timer; |
-import java.util.TimerTask; |
-import java.util.concurrent.ExecutionException; |
- |
-import javax.annotation.Nullable; |
- |
-/** |
- * This class detects child accounts and enables special treatment for them. |
- */ |
-public class ChildAccountService { |
- |
- private static final String TAG = "cr.ChildAccountService"; |
- |
- /** |
- * The maximum amount of time to wait for the initial child account check, in milliseconds. |
- */ |
- private static final int CHILD_ACCOUNT_TIMEOUT_MS = 1000; |
- |
- private static ChildAccountService sChildAccountService; |
- |
- private final Context mContext; |
- |
- /** |
- * Non-null if the the child account status has been determined. |
- */ |
- private Boolean mHasChildAccount; |
- |
- /** |
- * Non-null while a child account check is in progress. Note that if the child account status |
- * has been previously determined, the externally visible status only changes when the check |
- * finishes. This means that before that, calls to {@link #checkHasChildAccount} and |
- * {@link #hasChildAccount} will return the last value, even if it is now stale. |
- */ |
- private AccountManagerFuture<Boolean> mAccountManagerFuture; |
- |
- /** |
- * Non-empty while the initial child account check is in progress. |
- */ |
- private final List<HasChildAccountCallback> mCallbacks = new ArrayList<>(); |
- |
- protected ChildAccountService(Context context) { |
- mContext = context; |
- AccountManager accountManager = AccountManager.get(mContext); |
- accountManager.addOnAccountsUpdatedListener(new OnAccountsUpdateListener() { |
- @Override |
- public void onAccountsUpdated(Account[] accounts) { |
- ThreadUtils.runOnUiThread(new Runnable() { |
- @Override |
- public void run() { |
- recheckChildAccountStatus(); |
- } |
- }); |
- } |
- }, null, false); |
- } |
- |
- /** |
- * Returns the shared ChildAccountService instance, creating one if necessary. |
- * |
- * @param context The context to initialize the ChildAccountService with. |
- * @return The shared instance. |
- */ |
- public static ChildAccountService getInstance(Context context) { |
- ThreadUtils.assertOnUiThread(); |
- if (sChildAccountService == null) { |
- sChildAccountService = new ChildAccountService(context.getApplicationContext()); |
- } |
- return sChildAccountService; |
- } |
- |
- /** |
- * A callback to return the result of {@link #checkHasChildAccount}. |
- */ |
- public static interface HasChildAccountCallback { |
- |
- /** |
- * @param hasChildAccount Whether there is exactly one child account on the device. |
- */ |
- public void onChildAccountChecked(boolean hasChildAccount); |
- |
- } |
- |
- /** |
- * Checks for the presence of child accounts on the device. |
- * |
- * @param callback Will be called with the result (see |
- * {@link HasChildAccountCallback#onChildAccountChecked}). The callback is guaranteed |
- * to be called on a future turn of the event loop, even if the result can be |
- * determined immediately. |
- */ |
- public void checkHasChildAccount(final HasChildAccountCallback callback) { |
- if (mHasChildAccount != null || maybeUpdatePredeterminedChildAccountStatus()) { |
- postCallback(callback); |
- return; |
- } |
- mCallbacks.add(callback); |
- if (mAccountManagerFuture == null) requestChildAccountStatus(); |
- } |
- |
- private void postCallback(final HasChildAccountCallback callback) { |
- final boolean hasChildAccount = mHasChildAccount; |
- ThreadUtils.postOnUiThread(new Runnable() { |
- @Override |
- public void run() { |
- callback.onChildAccountChecked(hasChildAccount); |
- } |
- }); |
- } |
- |
- /** |
- * Updates the child account status if it can be determined immediately. |
- * |
- * @return Whether the child account status was updated. |
- */ |
- private boolean maybeUpdatePredeterminedChildAccountStatus() { |
- Boolean predeterminedChildAccountStatus = getPredeterminedChildStatus(); |
- if (predeterminedChildAccountStatus == null) return false; |
- setHasChildAccount(predeterminedChildAccountStatus); |
- return true; |
- } |
- |
- /** |
- * @return The child account status if it can be determined immediately, or null otherwise. |
- */ |
- @Nullable |
- private Boolean getPredeterminedChildStatus() { |
- if (!isChildAccountDetectionEnabled()) { |
- Log.v(TAG, "Child account detection disabled"); |
- return false; |
- } |
- Account[] googleAccounts = |
- AccountManagerHelper.get(mContext).getGoogleAccounts(); |
- if (googleAccounts.length != 1) { |
- if (CommandLine.getInstance().hasSwitch(ChromeSwitches.CHILD_ACCOUNT)) { |
- Log.w(TAG, "Ignoring --" + ChromeSwitches.CHILD_ACCOUNT + " command line flag " |
- + "because there are " + googleAccounts.length + " Google accounts on the " |
- + "device"); |
- } else { |
- Log.v(TAG, googleAccounts.length + " Google accounts on the device"); |
- } |
- return false; |
- } |
- String childAccountName = |
- CommandLine.getInstance().getSwitchValue(ChromeSwitches.CHILD_ACCOUNT); |
- String accountName = googleAccounts[0].name; |
- if (childAccountName != null && accountName.equals(childAccountName)) { |
- Log.v(TAG, "Child account forced via command line for " + childAccountName); |
- return true; |
- } |
- return null; |
- } |
- |
- private void requestChildAccountStatus() { |
- assert mAccountManagerFuture == null; |
- |
- final Timer timer = new Timer(); |
- final int traceId = System.identityHashCode(this); |
- TraceEvent.startAsync("ChildAccountService.checkFeatures", traceId); |
- AccountManagerHelper accountManagerHelper = AccountManagerHelper.get(mContext); |
- final AccountManagerFuture<Boolean> future = accountManagerHelper.checkChildAccount( |
- accountManagerHelper.getSingleGoogleAccount(), |
- new AccountManagerCallback<Boolean>() { |
- @Override |
- public void run(AccountManagerFuture<Boolean> future) { |
- TraceEvent.finishAsync("ChildAccountService.checkFeatures", traceId); |
- |
- timer.cancel(); |
- |
- assert future.isDone(); |
- |
- // Ignore any future that is not the current one. |
- if (future == mAccountManagerFuture) { |
- setHasChildAccount(getFutureResult()); |
- } |
- } |
- }); |
- |
- // Add a timeout during the initial check, to avoid blocking startup for too long. |
- if (mHasChildAccount == null) { |
- timer.schedule(new TimerTask() { |
- @Override |
- public void run() { |
- if (!future.isDone()) { |
- Log.v(TAG, "AM request timed out"); |
- future.cancel(true); |
- } |
- } |
- }, CHILD_ACCOUNT_TIMEOUT_MS); |
- } |
- mAccountManagerFuture = future; |
- } |
- |
- private boolean getFutureResult() { |
- boolean result = false; |
- try { |
- result = mAccountManagerFuture.getResult(); |
- Log.v(TAG, "AM future result:" + result); |
- } catch (OperationCanceledException e) { |
- Log.e(TAG, "Timed out fetching child account flag: ", e); |
- } catch (AuthenticatorException | IOException e) { |
- Log.e(TAG, "Error while fetching child account flag: ", e); |
- } finally { |
- mAccountManagerFuture = null; |
- } |
- return result; |
- } |
- |
- /** |
- * Returns whether there is a child account on the device. This method should |
- * only be called after the result has been determined (usually using |
- * {@link #checkHasChildAccount} or {@link #waitUntilFinished} to block). |
- * |
- * @return Whether there is a child account on the device. |
- */ |
- public boolean hasChildAccount() { |
- ThreadUtils.assertOnUiThread(); |
- |
- return mHasChildAccount; |
- } |
- |
- /** |
- * Waits until we have determined the child account status. Usually you should use callbacks |
- * instead of this method, see {@link #checkHasChildAccount}. |
- */ |
- public void waitUntilFinished() { |
- ThreadUtils.assertOnUiThread(); |
- |
- if (mHasChildAccount != null) return; |
- assert mAccountManagerFuture != null; |
- |
- TraceEvent.begin("ChildAccountService.waitUntilFinished"); |
- // This will block in getFutureResult(), but that may only happen on a background thread. |
- try { |
- AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() { |
- @Override |
- protected Boolean doInBackground(Void... params) { |
- return getFutureResult(); |
- } |
- }; |
- setHasChildAccount(task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR).get()); |
- } catch (ExecutionException e) { |
- Log.w(TAG, "Error while fetching child account flag: ", e); |
- } catch (InterruptedException e) { |
- Log.w(TAG, "Interrupted while fetching child account flag: ", e); |
- } |
- TraceEvent.end("ChildAccountService.waitUntilFinished"); |
- } |
- |
- /** |
- * If this returns false, Chrome will assume there are no child accounts on the device, |
- * and no further checks will be made, which has the effect of a kill switch. |
- * Can be overridden by subclasses to avoid native calls in testing. |
- * |
- * @return Whether child account detection is enabled. |
- */ |
- protected boolean isChildAccountDetectionEnabled() { |
- return nativeIsChildAccountDetectionEnabled(); |
- } |
- |
- private void setHasChildAccount(boolean hasChildAccount) { |
- Boolean oldHasChildAccount = mHasChildAccount; |
- mHasChildAccount = hasChildAccount; |
- for (HasChildAccountCallback callback : mCallbacks) { |
- postCallback(callback); |
- } |
- mCallbacks.clear(); |
- |
- onChildAccountStatusUpdated(oldHasChildAccount); |
- } |
- |
- /** |
- * Called when the child account status has been determined or updated. |
- * Can be overridden by subclasses to avoid native calls and calls into dependencies in testing. |
- * |
- * @param oldValue The old child account status. This is null when the child account status |
- * has been determined for the first time after the browser has started. |
- */ |
- protected void onChildAccountStatusUpdated(Boolean oldValue) { |
- Log.v(TAG, "hasChildAccount: " + mHasChildAccount + " oldHasChildAccount: " + oldValue); |
- if (mHasChildAccount) { |
- if (oldValue == null) { |
- // This is the first time we have determined the child account status, |
- // which means the browser is starting up. The startup code will sign in |
- // and call us back in onChildAccountSigninComplete(). |
- return; |
- } |
- if (!oldValue.booleanValue()) { |
- // We have switched from no child account to child account while the browser |
- // is running. Sign in (which will call us back in onChildAccountSigninComplete()). |
- SigninManager signinManager = SigninManager.get(mContext); |
- Account account = AccountManagerHelper.get(mContext).getSingleGoogleAccount(); |
- signinManager.signInToSelectedAccount(null, account, |
- SigninManager.SIGNIN_TYPE_FORCED_CHILD_ACCOUNT, |
- SigninManager.SIGNIN_SYNC_IMMEDIATELY, false, null); |
- return; |
- } |
- } |
- // Fallthrough for all other cases: Propagate child account status to native code. |
- // This is a no-op if the child account status does not change. |
- nativeSetIsChildAccount(mHasChildAccount); |
- } |
- |
- /** |
- * Called when the browser has been signed in to the child account. |
- */ |
- public void onChildAccountSigninComplete() { |
- nativeSetIsChildAccount(true); |
- } |
- |
- @VisibleForTesting |
- void recheckChildAccountStatus() { |
- // Cancel the AccountManagerFuture if it is running. |
- if (mAccountManagerFuture != null) { |
- mAccountManagerFuture.cancel(true); |
- mAccountManagerFuture = null; |
- } |
- if (!maybeUpdatePredeterminedChildAccountStatus()) { |
- requestChildAccountStatus(); |
- } |
- } |
- |
- @CalledByNative |
- private static void onInvalidationReceived() { |
- assert ThreadUtils.runningOnUiThread(); |
- if (sChildAccountService == null) return; |
- sChildAccountService.recheckChildAccountStatus(); |
- } |
- |
- private native boolean nativeIsChildAccountDetectionEnabled(); |
- |
- private native void nativeSetIsChildAccount(boolean isChild); |
-} |