// 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.services;
import android.accounts.AccountManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.Log;
import org.chromium.base.ObserverList;
import org.chromium.base.ThreadUtils;
import org.chromium.base.annotations.SuppressFBWarnings;
import org.chromium.base.library_loader.LibraryProcessType;
import org.chromium.base.library_loader.ProcessInitException;
import org.chromium.chrome.browser.ChromeApplication;
import org.chromium.chrome.browser.init.BrowserParts;
import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
import org.chromium.chrome.browser.init.EmptyBrowserParts;
import org.chromium.chrome.browser.signin.AccountTrackerService;
import org.chromium.chrome.browser.signin.SigninHelper;
import org.chromium.content.browser.BrowserStartupController;
import org.chromium.content.browser.BrowserStartupController.StartupCallback;
/**
* This receiver is notified when accounts are added, accounts are removed, or
* an account's credentials (saved password, etc) are changed.
*/
public class AccountsChangedReceiver extends BroadcastReceiver {
private static final String TAG = "AccountsChangedRx";
/**
* Receives a callback whenever {@link AccountManager#LOGIN_ACCOUNTS_CHANGED_ACTION} is
* broadcasted. Use {@link #addObserver} and {@link #removeObserver} to update registrations.
*
* The callback will only ever be called after the browser process has been initialized.
*/
public interface AccountsChangedObserver {
/**
* Called when {@link AccountManager#LOGIN_ACCOUNTS_CHANGED_ACTION} is triggered.
* @param context the application context.
* @param intent the broadcasted intent.
*/
void onAccountsChanged(Context context, Intent intent);
}
private static ObserverList<AccountsChangedObserver> sObservers = new ObserverList<>();
/**
* Adds an observer to the {@link AccountManager#LOGIN_ACCOUNTS_CHANGED_ACTION} broadcasts.
* @param observer the observer to add.
*/
public static void addObserver(AccountsChangedObserver observer) {
sObservers.addObserver(observer);
}
/**
* Removes an observer from the {@link AccountManager#LOGIN_ACCOUNTS_CHANGED_ACTION} broadcasts.
* @param observer the observer to add.
*/
public static void removeObserver(AccountsChangedObserver observer) {
sObservers.removeObserver(observer);
}
@Override
public void onReceive(Context context, final Intent intent) {
final Context appContext = context.getApplicationContext();
AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
SigninHelper.updateAccountRenameData(appContext);
return null;
}
@Override
protected void onPostExecute(Void result) {
continueHandleAccountChangeIfNeeded(appContext, intent);
}
};
task.execute();
}
private void continueHandleAccountChangeIfNeeded(final Context context, final Intent intent) {
if (!AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION.equals(intent.getAction())) return;
AccountTrackerService.get(context).invalidateAccountSeedStatus(
false /* don't refresh right now */);
boolean isChromeVisible = ApplicationStatus.hasVisibleActivities();
if (isChromeVisible) {
startBrowserIfNeededAndValidateAccounts(context);
} else {
// Notify SigninHelper of changed accounts (via shared prefs).
SigninHelper.markAccountsChangedPref(context);
}
notifyAccountsChangedOnBrowserStartup(context, intent);
}
@SuppressFBWarnings("DM_EXIT")
private static void startBrowserIfNeededAndValidateAccounts(final Context context) {
BrowserParts parts = new EmptyBrowserParts() {
@Override
public void finishNativeInitialization() {
ThreadUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
SigninHelper.get(context).validateAccountSettings(true);
}
});
}
@Override
public void onStartupFailure() {
// Startup failed. So notify SigninHelper of changed accounts via
// shared prefs.
SigninHelper.markAccountsChangedPref(context);
}
};
try {
ChromeBrowserInitializer.getInstance(context).handlePreNativeStartup(parts);
ChromeBrowserInitializer.getInstance(context).handlePostNativeStartup(true, parts);
} catch (ProcessInitException e) {
Log.e(TAG, "Unable to load native library.", e);
ChromeApplication.reportStartupErrorAndExit(e);
}
}
private static void notifyAccountsChangedOnBrowserStartup(
final Context context, final Intent intent) {
StartupCallback notifyAccountsChangedCallback = new StartupCallback() {
@Override
public void onSuccess(boolean alreadyStarted) {
for (AccountsChangedObserver observer : sObservers) {
observer.onAccountsChanged(context, intent);
}
}
@Override
public void onFailure() {
// Startup failed, so ignore call.
}
};
// If the browser process has already been loaded, a task will be posted immediately to
// call the |notifyAccountsChangedCallback| passed in as a parameter.
BrowserStartupController.get(context, LibraryProcessType.PROCESS_BROWSER)
.addStartupCompletedObserver(notifyAccountsChangedCallback);
}
}