Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1323)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java

Issue 1341423005: ChildAccountService[Java] delegates everything to native side. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@child
Patch Set: nits Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java b/chrome/android/java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java
index 72bc73819ae8571becc1f77dd3bbaca6bd8a5482..ffbb7faf647a7bcfa9683fb48daa219cbfb7723b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java
@@ -10,90 +10,29 @@ import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
-import android.content.Intent;
-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.base.annotations.CalledByNative;
-import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.services.AccountsChangedReceiver;
-import org.chromium.chrome.browser.signin.SigninManager;
import org.chromium.sync.signin.AccountManagerHelper;
-import org.chromium.sync.signin.ChromeSigninController;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Timer;
-import java.util.TimerTask;
-
-import javax.annotation.Nullable;
/**
- * This class detects child accounts and enables special treatment for them.
+ * This class serves as a simple interface for querying the child account information.
+ * It has two methods namely, checkHasChildAccount(...) which is asynchronous and queries the
+ * system directly for the information and the synchronous isChildAccount() which asks the native
+ * side assuming it has been set correctly already.
+ *
+ * The former method is used by ForcedSigninProcessor and FirstRunFlowSequencer to detect child
+ * accounts since the native side is only activated on signing in.
+ * Once signed in by the ForcedSigninProcessor, the ChildAccountInfoFetcher will notify the native
+ * side and also takes responsibility for monitoring changes and taking a suitable action.
*/
public class ChildAccountService {
+ private static final String TAG = "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;
- AccountsChangedReceiver.addObserver(
- new AccountsChangedReceiver.AccountsChangedObserver() {
- @Override
- public void onAccountsChanged(Context context, Intent intent) {
- ThreadUtils.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- recheckChildAccountStatus();
- }
- });
- }
- });
- }
-
- /**
- * 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;
+ private ChildAccountService() {
+ // Only for static usage.
}
/**
@@ -111,234 +50,53 @@ public class ChildAccountService {
/**
* 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.
+ * @param callback A callback which will be called with the result.
*/
- public void checkHasChildAccount(final HasChildAccountCallback callback) {
- if (mHasChildAccount != null || maybeUpdatePredeterminedChildAccountStatus()) {
- postCallback(callback);
+ public static void checkHasChildAccount(
+ Context context, final HasChildAccountCallback callback) {
+ ThreadUtils.assertOnUiThread();
+ if (!nativeIsChildAccountDetectionEnabled()) {
+ callback.onChildAccountChecked(false);
return;
}
- mCallbacks.add(callback);
- if (mAccountManagerFuture == null) requestChildAccountStatus();
- }
-
- private void postCallback(final HasChildAccountCallback callback) {
- final boolean hasChildAccount = mHasChildAccount;
- ThreadUtils.postOnUiThread(new Runnable() {
+ AccountManagerHelper helper = AccountManagerHelper.get(context);
+ Account[] accounts = helper.getGoogleAccounts();
+ if (accounts.length != 1) {
+ callback.onChildAccountChecked(false);
+ return;
+ }
+ helper.checkChildAccount(accounts[0], new AccountManagerCallback<Boolean>() {
@Override
- public void run() {
- callback.onChildAccountChecked(hasChildAccount);
+ public void run(AccountManagerFuture<Boolean> future) {
+ assert future.isDone();
+ boolean hasFeatures = false;
+ try {
+ hasFeatures = future.getResult();
+ } catch (AuthenticatorException | IOException e) {
+ Log.e(TAG, "Error while checking features: ", e);
+ } catch (OperationCanceledException e) {
+ Log.e(TAG, "Checking features was cancelled. This should not happen.");
+ }
+ callback.onChildAccountChecked(hasFeatures);
}
});
}
/**
- * 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 (!nativeIsChildAccountDetectionEnabled()) {
- Log.v(TAG, "Child account detection disabled");
- return false;
- }
- AccountManagerHelper accountManagerHelper = AccountManagerHelper.get(mContext);
- // This isn't strictly necessary, as getGoogleAccounts() will return an empty list if the
- // GET_ACCOUNTS permission is not granted, but it makes the behavior explicit.
- if (!accountManagerHelper.hasGetAccountsPermission()) {
- Log.v(TAG, "GET_ACCOUNTS permission not granted");
- return false;
- }
- Account[] googleAccounts = accountManagerHelper.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. If the initial check has not
- * completed yet, this will return a cached value from the last run (which might be stale now).
- * Because this method might call into native code, it may only be called after the native
- * library and the profile have been loaded.
- *
- * @return Whether there is a child account on the device.
- */
- public boolean hasChildAccount() {
- ThreadUtils.assertOnUiThread();
-
- if (mHasChildAccount == null) return nativeGetIsChildAccount();
-
- return mHasChildAccount;
- }
-
- 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.
+ * Returns the previously determined value of whether there is a child account on the device.
+ * Should only be called after the native library and profile have been loaded.
*
- * @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. If we are not signed in yet, the startup code will
- // sign in and call us back in onChildAccountSigninComplete().
- if (ChromeSigninController.get(mContext).getSignedInUser() == null) return;
- } else 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.
+ * @return The previously determined value of whether there is a child account on the device.
*/
- 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();
- }
+ public static boolean isChildAccount() {
+ return nativeIsChildAccount();
}
- @CalledByNative
- private static void onInvalidationReceived() {
- assert ThreadUtils.runningOnUiThread();
- if (sChildAccountService == null) return;
- sChildAccountService.recheckChildAccountStatus();
- }
+ private static native boolean nativeIsChildAccount();
/**
* 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 native boolean nativeIsChildAccountDetectionEnabled();
-
- /**
- * Returns the previously determined value of whether there is a child account on the device.
- * Can be overridden by subclasses to avoid native calls in testing.
- *
- * @return The previously determined value of whether there is a child account on the device.
- */
- protected native boolean nativeGetIsChildAccount();
-
- private native void nativeSetIsChildAccount(boolean isChild);
+ private static native boolean nativeIsChildAccountDetectionEnabled();
}

Powered by Google App Engine
This is Rietveld 408576698