diff options
author | Jeff Sharkey <jsharkey@android.com> | 2012-11-15 14:01:46 -0800 |
---|---|---|
committer | Amith Yamasani <yamasani@google.com> | 2012-12-11 09:50:50 -0800 |
commit | 7a96c39c510923ef73bbb06ab20109f0168b8eb1 (patch) | |
tree | 70fd480a814add0fdcb274540e1f421806020f24 /core | |
parent | 151cb90c6093d5b4371b9367b507f8aa7c1a4370 (diff) | |
download | frameworks_base-7a96c39c510923ef73bbb06ab20109f0168b8eb1.zip frameworks_base-7a96c39c510923ef73bbb06ab20109f0168b8eb1.tar.gz frameworks_base-7a96c39c510923ef73bbb06ab20109f0168b8eb1.tar.bz2 |
Move lingering services to services.jar.
This helps reduce the pressure on framework.jar, and makes it clear
that it should only be used by the system_server.
Bug: 7333397
Change-Id: I0858904239535380fbf30562b793e277d8c3f054
Diffstat (limited to 'core')
23 files changed, 38 insertions, 10702 deletions
diff --git a/core/java/android/accounts/AccountAuthenticatorCache.java b/core/java/android/accounts/AccountAuthenticatorCache.java deleted file mode 100644 index f937cde..0000000 --- a/core/java/android/accounts/AccountAuthenticatorCache.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.accounts; - -import android.content.Context; -import android.content.pm.PackageManager; -import android.content.pm.RegisteredServicesCache; -import android.content.pm.XmlSerializerAndParser; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.text.TextUtils; -import android.util.AttributeSet; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -import java.io.IOException; - -/** - * A cache of services that export the {@link IAccountAuthenticator} interface. This cache - * is built by interrogating the {@link PackageManager} and is updated as packages are added, - * removed and changed. The authenticators are referred to by their account type and - * are made available via the {@link RegisteredServicesCache#getServiceInfo} method. - * @hide - */ -/* package private */ class AccountAuthenticatorCache - extends RegisteredServicesCache<AuthenticatorDescription> - implements IAccountAuthenticatorCache { - private static final String TAG = "Account"; - private static final MySerializer sSerializer = new MySerializer(); - - public AccountAuthenticatorCache(Context context) { - super(context, AccountManager.ACTION_AUTHENTICATOR_INTENT, - AccountManager.AUTHENTICATOR_META_DATA_NAME, - AccountManager.AUTHENTICATOR_ATTRIBUTES_NAME, sSerializer); - } - - public AuthenticatorDescription parseServiceAttributes(Resources res, - String packageName, AttributeSet attrs) { - TypedArray sa = res.obtainAttributes(attrs, - com.android.internal.R.styleable.AccountAuthenticator); - try { - final String accountType = - sa.getString(com.android.internal.R.styleable.AccountAuthenticator_accountType); - final int labelId = sa.getResourceId( - com.android.internal.R.styleable.AccountAuthenticator_label, 0); - final int iconId = sa.getResourceId( - com.android.internal.R.styleable.AccountAuthenticator_icon, 0); - final int smallIconId = sa.getResourceId( - com.android.internal.R.styleable.AccountAuthenticator_smallIcon, 0); - final int prefId = sa.getResourceId( - com.android.internal.R.styleable.AccountAuthenticator_accountPreferences, 0); - final boolean customTokens = sa.getBoolean( - com.android.internal.R.styleable.AccountAuthenticator_customTokens, false); - if (TextUtils.isEmpty(accountType)) { - return null; - } - return new AuthenticatorDescription(accountType, packageName, labelId, iconId, - smallIconId, prefId, customTokens); - } finally { - sa.recycle(); - } - } - - private static class MySerializer implements XmlSerializerAndParser<AuthenticatorDescription> { - public void writeAsXml(AuthenticatorDescription item, XmlSerializer out) - throws IOException { - out.attribute(null, "type", item.type); - } - - public AuthenticatorDescription createFromXml(XmlPullParser parser) - throws IOException, XmlPullParserException { - return AuthenticatorDescription.newKey(parser.getAttributeValue(null, "type")); - } - } -} diff --git a/core/java/android/accounts/AccountAuthenticatorResponse.java b/core/java/android/accounts/AccountAuthenticatorResponse.java index 614e139..41f26ac 100644 --- a/core/java/android/accounts/AccountAuthenticatorResponse.java +++ b/core/java/android/accounts/AccountAuthenticatorResponse.java @@ -33,7 +33,7 @@ public class AccountAuthenticatorResponse implements Parcelable { /** * @hide */ - /* package private */ AccountAuthenticatorResponse(IAccountAuthenticatorResponse response) { + public AccountAuthenticatorResponse(IAccountAuthenticatorResponse response) { mAccountAuthenticatorResponse = response; } diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java deleted file mode 100644 index 2b1a2b2..0000000 --- a/core/java/android/accounts/AccountManagerService.java +++ /dev/null @@ -1,2548 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.accounts; - -import android.Manifest; -import android.app.ActivityManager; -import android.app.ActivityManagerNative; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.ServiceConnection; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.RegisteredServicesCache; -import android.content.pm.RegisteredServicesCacheListener; -import android.content.pm.UserInfo; -import android.database.Cursor; -import android.database.DatabaseUtils; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.os.Binder; -import android.os.Bundle; -import android.os.Environment; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.UserHandle; -import android.os.UserManager; -import android.text.TextUtils; -import android.util.Log; -import android.util.Pair; -import android.util.Slog; -import android.util.SparseArray; - -import com.android.internal.R; -import com.android.internal.util.IndentingPrintWriter; -import com.google.android.collect.Lists; -import com.google.android.collect.Sets; - -import java.io.File; -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -/** - * A system service that provides account, password, and authtoken management for all - * accounts on the device. Some of these calls are implemented with the help of the corresponding - * {@link IAccountAuthenticator} services. This service is not accessed by users directly, - * instead one uses an instance of {@link AccountManager}, which can be accessed as follows: - * AccountManager accountManager = AccountManager.get(context); - * @hide - */ -public class AccountManagerService - extends IAccountManager.Stub - implements RegisteredServicesCacheListener<AuthenticatorDescription> { - private static final String TAG = "AccountManagerService"; - - private static final int TIMEOUT_DELAY_MS = 1000 * 60; - private static final String DATABASE_NAME = "accounts.db"; - private static final int DATABASE_VERSION = 4; - - private final Context mContext; - - private final PackageManager mPackageManager; - private UserManager mUserManager; - - private HandlerThread mMessageThread; - private final MessageHandler mMessageHandler; - - // Messages that can be sent on mHandler - private static final int MESSAGE_TIMED_OUT = 3; - - private final IAccountAuthenticatorCache mAuthenticatorCache; - - private static final String TABLE_ACCOUNTS = "accounts"; - private static final String ACCOUNTS_ID = "_id"; - private static final String ACCOUNTS_NAME = "name"; - private static final String ACCOUNTS_TYPE = "type"; - private static final String ACCOUNTS_TYPE_COUNT = "count(type)"; - private static final String ACCOUNTS_PASSWORD = "password"; - - private static final String TABLE_AUTHTOKENS = "authtokens"; - private static final String AUTHTOKENS_ID = "_id"; - private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id"; - private static final String AUTHTOKENS_TYPE = "type"; - private static final String AUTHTOKENS_AUTHTOKEN = "authtoken"; - - private static final String TABLE_GRANTS = "grants"; - private static final String GRANTS_ACCOUNTS_ID = "accounts_id"; - private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type"; - private static final String GRANTS_GRANTEE_UID = "uid"; - - private static final String TABLE_EXTRAS = "extras"; - private static final String EXTRAS_ID = "_id"; - private static final String EXTRAS_ACCOUNTS_ID = "accounts_id"; - private static final String EXTRAS_KEY = "key"; - private static final String EXTRAS_VALUE = "value"; - - private static final String TABLE_META = "meta"; - private static final String META_KEY = "key"; - private static final String META_VALUE = "value"; - - private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION = - new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT}; - private static final Intent ACCOUNTS_CHANGED_INTENT; - - private static final String COUNT_OF_MATCHING_GRANTS = "" - + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS - + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID - + " AND " + GRANTS_GRANTEE_UID + "=?" - + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?" - + " AND " + ACCOUNTS_NAME + "=?" - + " AND " + ACCOUNTS_TYPE + "=?"; - - private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT = - AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)"; - private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE, - AUTHTOKENS_AUTHTOKEN}; - - private static final String SELECTION_USERDATA_BY_ACCOUNT = - EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)"; - private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE}; - - private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>(); - private final AtomicInteger mNotificationIds = new AtomicInteger(1); - - static class UserAccounts { - private final int userId; - private final DatabaseHelper openHelper; - private final HashMap<Pair<Pair<Account, String>, Integer>, Integer> - credentialsPermissionNotificationIds = - new HashMap<Pair<Pair<Account, String>, Integer>, Integer>(); - private final HashMap<Account, Integer> signinRequiredNotificationIds = - new HashMap<Account, Integer>(); - private final Object cacheLock = new Object(); - /** protected by the {@link #cacheLock} */ - private final HashMap<String, Account[]> accountCache = - new LinkedHashMap<String, Account[]>(); - /** protected by the {@link #cacheLock} */ - private HashMap<Account, HashMap<String, String>> userDataCache = - new HashMap<Account, HashMap<String, String>>(); - /** protected by the {@link #cacheLock} */ - private HashMap<Account, HashMap<String, String>> authTokenCache = - new HashMap<Account, HashMap<String, String>>(); - - UserAccounts(Context context, int userId) { - this.userId = userId; - synchronized (cacheLock) { - openHelper = new DatabaseHelper(context, userId); - } - } - } - - private final SparseArray<UserAccounts> mUsers = new SparseArray<UserAccounts>(); - - private static AtomicReference<AccountManagerService> sThis = - new AtomicReference<AccountManagerService>(); - private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{}; - - static { - ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION); - ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - } - - - /** - * This should only be called by system code. One should only call this after the service - * has started. - * @return a reference to the AccountManagerService instance - * @hide - */ - public static AccountManagerService getSingleton() { - return sThis.get(); - } - - public AccountManagerService(Context context) { - this(context, context.getPackageManager(), new AccountAuthenticatorCache(context)); - } - - public AccountManagerService(Context context, PackageManager packageManager, - IAccountAuthenticatorCache authenticatorCache) { - mContext = context; - mPackageManager = packageManager; - - mMessageThread = new HandlerThread("AccountManagerService"); - mMessageThread.start(); - mMessageHandler = new MessageHandler(mMessageThread.getLooper()); - - mAuthenticatorCache = authenticatorCache; - mAuthenticatorCache.setListener(this, null /* Handler */); - - sThis.set(this); - - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); - intentFilter.addDataScheme("package"); - mContext.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context1, Intent intent) { - purgeOldGrantsAll(); - } - }, intentFilter); - - IntentFilter userFilter = new IntentFilter(); - userFilter.addAction(Intent.ACTION_USER_REMOVED); - mContext.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - onUserRemoved(intent); - } - }, userFilter); - } - - public void systemReady() { - } - - private UserManager getUserManager() { - if (mUserManager == null) { - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - } - return mUserManager; - } - - private UserAccounts initUser(int userId) { - synchronized (mUsers) { - UserAccounts accounts = mUsers.get(userId); - if (accounts == null) { - accounts = new UserAccounts(mContext, userId); - mUsers.append(userId, accounts); - purgeOldGrants(accounts); - validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */); - } - return accounts; - } - } - - private void purgeOldGrantsAll() { - synchronized (mUsers) { - for (int i = 0; i < mUsers.size(); i++) { - purgeOldGrants(mUsers.valueAt(i)); - } - } - } - - private void purgeOldGrants(UserAccounts accounts) { - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - final Cursor cursor = db.query(TABLE_GRANTS, - new String[]{GRANTS_GRANTEE_UID}, - null, null, GRANTS_GRANTEE_UID, null, null); - try { - while (cursor.moveToNext()) { - final int uid = cursor.getInt(0); - final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null; - if (packageExists) { - continue; - } - Log.d(TAG, "deleting grants for UID " + uid - + " because its package is no longer installed"); - db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?", - new String[]{Integer.toString(uid)}); - } - } finally { - cursor.close(); - } - } - } - - /** - * Validate internal set of accounts against installed authenticators for - * given user. Clears cached authenticators before validating. - */ - public void validateAccounts(int userId) { - final UserAccounts accounts = getUserAccounts(userId); - - // Invalidate user-specific cache to make sure we catch any - // removed authenticators. - validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */); - } - - /** - * Validate internal set of accounts against installed authenticators for - * given user. Clear cached authenticators before validating when requested. - */ - private void validateAccountsInternal( - UserAccounts accounts, boolean invalidateAuthenticatorCache) { - if (invalidateAuthenticatorCache) { - mAuthenticatorCache.invalidateCache(accounts.userId); - } - - final HashSet<AuthenticatorDescription> knownAuth = Sets.newHashSet(); - for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : - mAuthenticatorCache.getAllServices(accounts.userId)) { - knownAuth.add(service.type); - } - - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - boolean accountDeleted = false; - Cursor cursor = db.query(TABLE_ACCOUNTS, - new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME}, - null, null, null, null, null); - try { - accounts.accountCache.clear(); - final HashMap<String, ArrayList<String>> accountNamesByType = - new LinkedHashMap<String, ArrayList<String>>(); - while (cursor.moveToNext()) { - final long accountId = cursor.getLong(0); - final String accountType = cursor.getString(1); - final String accountName = cursor.getString(2); - - if (!knownAuth.contains(AuthenticatorDescription.newKey(accountType))) { - Slog.w(TAG, "deleting account " + accountName + " because type " - + accountType + " no longer has a registered authenticator"); - db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null); - accountDeleted = true; - final Account account = new Account(accountName, accountType); - accounts.userDataCache.remove(account); - accounts.authTokenCache.remove(account); - } else { - ArrayList<String> accountNames = accountNamesByType.get(accountType); - if (accountNames == null) { - accountNames = new ArrayList<String>(); - accountNamesByType.put(accountType, accountNames); - } - accountNames.add(accountName); - } - } - for (Map.Entry<String, ArrayList<String>> cur - : accountNamesByType.entrySet()) { - final String accountType = cur.getKey(); - final ArrayList<String> accountNames = cur.getValue(); - final Account[] accountsForType = new Account[accountNames.size()]; - int i = 0; - for (String accountName : accountNames) { - accountsForType[i] = new Account(accountName, accountType); - ++i; - } - accounts.accountCache.put(accountType, accountsForType); - } - } finally { - cursor.close(); - if (accountDeleted) { - sendAccountsChangedBroadcast(accounts.userId); - } - } - } - } - - private UserAccounts getUserAccountsForCaller() { - return getUserAccounts(UserHandle.getCallingUserId()); - } - - protected UserAccounts getUserAccounts(int userId) { - synchronized (mUsers) { - UserAccounts accounts = mUsers.get(userId); - if (accounts == null) { - accounts = initUser(userId); - mUsers.append(userId, accounts); - } - return accounts; - } - } - - private void onUserRemoved(Intent intent) { - int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); - if (userId < 1) return; - - UserAccounts accounts; - synchronized (mUsers) { - accounts = mUsers.get(userId); - mUsers.remove(userId); - } - if (accounts == null) { - File dbFile = new File(getDatabaseName(userId)); - dbFile.delete(); - return; - } - - synchronized (accounts.cacheLock) { - accounts.openHelper.close(); - File dbFile = new File(getDatabaseName(userId)); - dbFile.delete(); - } - } - - @Override - public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) { - Slog.d(TAG, "onServiceChanged() for userId " + userId); - validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */); - } - - public String getPassword(Account account) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "getPassword: " + account - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (account == null) throw new IllegalArgumentException("account is null"); - checkAuthenticateAccountsPermission(account); - - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - return readPasswordInternal(accounts, account); - } finally { - restoreCallingIdentity(identityToken); - } - } - - private String readPasswordInternal(UserAccounts accounts, Account account) { - if (account == null) { - return null; - } - - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); - Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD}, - ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", - new String[]{account.name, account.type}, null, null, null); - try { - if (cursor.moveToNext()) { - return cursor.getString(0); - } - return null; - } finally { - cursor.close(); - } - } - } - - public String getUserData(Account account, String key) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "getUserData: " + account - + ", key " + key - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (account == null) throw new IllegalArgumentException("account is null"); - if (key == null) throw new IllegalArgumentException("key is null"); - checkAuthenticateAccountsPermission(account); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - return readUserDataInternal(accounts, account, key); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public AuthenticatorDescription[] getAuthenticatorTypes() { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "getAuthenticatorTypes: " - + "caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - final int userId = UserHandle.getCallingUserId(); - final long identityToken = clearCallingIdentity(); - try { - Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>> - authenticatorCollection = mAuthenticatorCache.getAllServices(userId); - AuthenticatorDescription[] types = - new AuthenticatorDescription[authenticatorCollection.size()]; - int i = 0; - for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator - : authenticatorCollection) { - types[i] = authenticator.type; - i++; - } - return types; - } finally { - restoreCallingIdentity(identityToken); - } - } - - public boolean addAccount(Account account, String password, Bundle extras) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "addAccount: " + account - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (account == null) throw new IllegalArgumentException("account is null"); - checkAuthenticateAccountsPermission(account); - - UserAccounts accounts = getUserAccountsForCaller(); - // fails if the account already exists - long identityToken = clearCallingIdentity(); - try { - return addAccountInternal(accounts, account, password, extras); - } finally { - restoreCallingIdentity(identityToken); - } - } - - private boolean addAccountInternal(UserAccounts accounts, Account account, String password, - Bundle extras) { - if (account == null) { - return false; - } - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - db.beginTransaction(); - try { - long numMatches = DatabaseUtils.longForQuery(db, - "select count(*) from " + TABLE_ACCOUNTS - + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", - new String[]{account.name, account.type}); - if (numMatches > 0) { - Log.w(TAG, "insertAccountIntoDatabase: " + account - + ", skipping since the account already exists"); - return false; - } - ContentValues values = new ContentValues(); - values.put(ACCOUNTS_NAME, account.name); - values.put(ACCOUNTS_TYPE, account.type); - values.put(ACCOUNTS_PASSWORD, password); - long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values); - if (accountId < 0) { - Log.w(TAG, "insertAccountIntoDatabase: " + account - + ", skipping the DB insert failed"); - return false; - } - if (extras != null) { - for (String key : extras.keySet()) { - final String value = extras.getString(key); - if (insertExtraLocked(db, accountId, key, value) < 0) { - Log.w(TAG, "insertAccountIntoDatabase: " + account - + ", skipping since insertExtra failed for key " + key); - return false; - } - } - } - db.setTransactionSuccessful(); - insertAccountIntoCacheLocked(accounts, account); - } finally { - db.endTransaction(); - } - sendAccountsChangedBroadcast(accounts.userId); - return true; - } - } - - private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) { - ContentValues values = new ContentValues(); - values.put(EXTRAS_KEY, key); - values.put(EXTRAS_ACCOUNTS_ID, accountId); - values.put(EXTRAS_VALUE, value); - return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values); - } - - public void hasFeatures(IAccountManagerResponse response, - Account account, String[] features) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "hasFeatures: " + account - + ", response " + response - + ", features " + stringArrayToString(features) - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (response == null) throw new IllegalArgumentException("response is null"); - if (account == null) throw new IllegalArgumentException("account is null"); - if (features == null) throw new IllegalArgumentException("features is null"); - checkReadAccountsPermission(); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - new TestFeaturesSession(accounts, response, account, features).bind(); - } finally { - restoreCallingIdentity(identityToken); - } - } - - private class TestFeaturesSession extends Session { - private final String[] mFeatures; - private final Account mAccount; - - public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response, - Account account, String[] features) { - super(accounts, response, account.type, false /* expectActivityLaunch */, - true /* stripAuthTokenFromResult */); - mFeatures = features; - mAccount = account; - } - - public void run() throws RemoteException { - try { - mAuthenticator.hasFeatures(this, mAccount, mFeatures); - } catch (RemoteException e) { - onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception"); - } - } - - public void onResult(Bundle result) { - IAccountManagerResponse response = getResponseAndClose(); - if (response != null) { - try { - if (result == null) { - response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle"); - return; - } - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " - + response); - } - final Bundle newResult = new Bundle(); - newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, - result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)); - response.onResult(newResult); - } catch (RemoteException e) { - // if the caller is dead then there is no one to care about remote exceptions - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "failure while notifying response", e); - } - } - } - } - - protected String toDebugString(long now) { - return super.toDebugString(now) + ", hasFeatures" - + ", " + mAccount - + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null); - } - } - - public void removeAccount(IAccountManagerResponse response, Account account) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "removeAccount: " + account - + ", response " + response - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (response == null) throw new IllegalArgumentException("response is null"); - if (account == null) throw new IllegalArgumentException("account is null"); - checkManageAccountsPermission(); - UserHandle user = Binder.getCallingUserHandle(); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - - cancelNotification(getSigninRequiredNotificationId(accounts, account), user); - synchronized(accounts.credentialsPermissionNotificationIds) { - for (Pair<Pair<Account, String>, Integer> pair: - accounts.credentialsPermissionNotificationIds.keySet()) { - if (account.equals(pair.first.first)) { - int id = accounts.credentialsPermissionNotificationIds.get(pair); - cancelNotification(id, user); - } - } - } - - try { - new RemoveAccountSession(accounts, response, account).bind(); - } finally { - restoreCallingIdentity(identityToken); - } - } - - private class RemoveAccountSession extends Session { - final Account mAccount; - public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response, - Account account) { - super(accounts, response, account.type, false /* expectActivityLaunch */, - true /* stripAuthTokenFromResult */); - mAccount = account; - } - - protected String toDebugString(long now) { - return super.toDebugString(now) + ", removeAccount" - + ", account " + mAccount; - } - - public void run() throws RemoteException { - mAuthenticator.getAccountRemovalAllowed(this, mAccount); - } - - public void onResult(Bundle result) { - if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT) - && !result.containsKey(AccountManager.KEY_INTENT)) { - final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT); - if (removalAllowed) { - removeAccountInternal(mAccounts, mAccount); - } - IAccountManagerResponse response = getResponseAndClose(); - if (response != null) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " - + response); - } - Bundle result2 = new Bundle(); - result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed); - try { - response.onResult(result2); - } catch (RemoteException e) { - // ignore - } - } - } - super.onResult(result); - } - } - - /* For testing */ - protected void removeAccountInternal(Account account) { - removeAccountInternal(getUserAccountsForCaller(), account); - } - - private void removeAccountInternal(UserAccounts accounts, Account account) { - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", - new String[]{account.name, account.type}); - removeAccountFromCacheLocked(accounts, account); - sendAccountsChangedBroadcast(accounts.userId); - } - } - - public void invalidateAuthToken(String accountType, String authToken) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "invalidateAuthToken: accountType " + accountType - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (accountType == null) throw new IllegalArgumentException("accountType is null"); - if (authToken == null) throw new IllegalArgumentException("authToken is null"); - checkManageAccountsOrUseCredentialsPermissions(); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - db.beginTransaction(); - try { - invalidateAuthTokenLocked(accounts, db, accountType, authToken); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db, - String accountType, String authToken) { - if (authToken == null || accountType == null) { - return; - } - Cursor cursor = db.rawQuery( - "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID - + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME - + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE - + " FROM " + TABLE_ACCOUNTS - + " JOIN " + TABLE_AUTHTOKENS - + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID - + " = " + AUTHTOKENS_ACCOUNTS_ID - + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND " - + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?", - new String[]{authToken, accountType}); - try { - while (cursor.moveToNext()) { - long authTokenId = cursor.getLong(0); - String accountName = cursor.getString(1); - String authTokenType = cursor.getString(2); - db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null); - writeAuthTokenIntoCacheLocked(accounts, db, new Account(accountName, accountType), - authTokenType, null); - } - } finally { - cursor.close(); - } - } - - private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type, - String authToken) { - if (account == null || type == null) { - return false; - } - cancelNotification(getSigninRequiredNotificationId(accounts, account), - new UserHandle(accounts.userId)); - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - db.beginTransaction(); - try { - long accountId = getAccountIdLocked(db, account); - if (accountId < 0) { - return false; - } - db.delete(TABLE_AUTHTOKENS, - AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?", - new String[]{type}); - ContentValues values = new ContentValues(); - values.put(AUTHTOKENS_ACCOUNTS_ID, accountId); - values.put(AUTHTOKENS_TYPE, type); - values.put(AUTHTOKENS_AUTHTOKEN, authToken); - if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) { - db.setTransactionSuccessful(); - writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken); - return true; - } - return false; - } finally { - db.endTransaction(); - } - } - } - - public String peekAuthToken(Account account, String authTokenType) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "peekAuthToken: " + account - + ", authTokenType " + authTokenType - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (account == null) throw new IllegalArgumentException("account is null"); - if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); - checkAuthenticateAccountsPermission(account); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - return readAuthTokenInternal(accounts, account, authTokenType); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void setAuthToken(Account account, String authTokenType, String authToken) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "setAuthToken: " + account - + ", authTokenType " + authTokenType - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (account == null) throw new IllegalArgumentException("account is null"); - if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); - checkAuthenticateAccountsPermission(account); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - saveAuthTokenToDatabase(accounts, account, authTokenType, authToken); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void setPassword(Account account, String password) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "setAuthToken: " + account - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (account == null) throw new IllegalArgumentException("account is null"); - checkAuthenticateAccountsPermission(account); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - setPasswordInternal(accounts, account, password); - } finally { - restoreCallingIdentity(identityToken); - } - } - - private void setPasswordInternal(UserAccounts accounts, Account account, String password) { - if (account == null) { - return; - } - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - db.beginTransaction(); - try { - final ContentValues values = new ContentValues(); - values.put(ACCOUNTS_PASSWORD, password); - final long accountId = getAccountIdLocked(db, account); - if (accountId >= 0) { - final String[] argsAccountId = {String.valueOf(accountId)}; - db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId); - db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId); - accounts.authTokenCache.remove(account); - db.setTransactionSuccessful(); - } - } finally { - db.endTransaction(); - } - sendAccountsChangedBroadcast(accounts.userId); - } - } - - private void sendAccountsChangedBroadcast(int userId) { - Log.i(TAG, "the accounts changed, sending broadcast of " - + ACCOUNTS_CHANGED_INTENT.getAction()); - mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId)); - } - - public void clearPassword(Account account) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "clearPassword: " + account - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (account == null) throw new IllegalArgumentException("account is null"); - checkManageAccountsPermission(); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - setPasswordInternal(accounts, account, null); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void setUserData(Account account, String key, String value) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "setUserData: " + account - + ", key " + key - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (key == null) throw new IllegalArgumentException("key is null"); - if (account == null) throw new IllegalArgumentException("account is null"); - checkAuthenticateAccountsPermission(account); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - setUserdataInternal(accounts, account, key, value); - } finally { - restoreCallingIdentity(identityToken); - } - } - - private void setUserdataInternal(UserAccounts accounts, Account account, String key, - String value) { - if (account == null || key == null) { - return; - } - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - db.beginTransaction(); - try { - long accountId = getAccountIdLocked(db, account); - if (accountId < 0) { - return; - } - long extrasId = getExtrasIdLocked(db, accountId, key); - if (extrasId < 0 ) { - extrasId = insertExtraLocked(db, accountId, key, value); - if (extrasId < 0) { - return; - } - } else { - ContentValues values = new ContentValues(); - values.put(EXTRAS_VALUE, value); - if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) { - return; - } - - } - writeUserDataIntoCacheLocked(accounts, db, account, key, value); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - } - - private void onResult(IAccountManagerResponse response, Bundle result) { - if (result == null) { - Log.e(TAG, "the result is unexpectedly null", new Exception()); - } - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " - + response); - } - try { - response.onResult(result); - } catch (RemoteException e) { - // if the caller is dead then there is no one to care about remote - // exceptions - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "failure while notifying response", e); - } - } - } - - public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType, - final String authTokenType) - throws RemoteException { - if (accountType == null) throw new IllegalArgumentException("accountType is null"); - if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); - - final int callingUid = getCallingUid(); - clearCallingIdentity(); - if (callingUid != android.os.Process.SYSTEM_UID) { - throw new SecurityException("can only call from system"); - } - UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid)); - long identityToken = clearCallingIdentity(); - try { - new Session(accounts, response, accountType, false, - false /* stripAuthTokenFromResult */) { - protected String toDebugString(long now) { - return super.toDebugString(now) + ", getAuthTokenLabel" - + ", " + accountType - + ", authTokenType " + authTokenType; - } - - public void run() throws RemoteException { - mAuthenticator.getAuthTokenLabel(this, authTokenType); - } - - public void onResult(Bundle result) { - if (result != null) { - String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL); - Bundle bundle = new Bundle(); - bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label); - super.onResult(bundle); - return; - } else { - super.onResult(result); - } - } - }.bind(); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void getAuthToken(IAccountManagerResponse response, final Account account, - final String authTokenType, final boolean notifyOnAuthFailure, - final boolean expectActivityLaunch, Bundle loginOptionsIn) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "getAuthToken: " + account - + ", response " + response - + ", authTokenType " + authTokenType - + ", notifyOnAuthFailure " + notifyOnAuthFailure - + ", expectActivityLaunch " + expectActivityLaunch - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (response == null) throw new IllegalArgumentException("response is null"); - if (account == null) throw new IllegalArgumentException("account is null"); - if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); - checkBinderPermission(Manifest.permission.USE_CREDENTIALS); - final UserAccounts accounts = getUserAccountsForCaller(); - final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; - authenticatorInfo = mAuthenticatorCache.getServiceInfo( - AuthenticatorDescription.newKey(account.type), accounts.userId); - final boolean customTokens = - authenticatorInfo != null && authenticatorInfo.type.customTokens; - - // skip the check if customTokens - final int callerUid = Binder.getCallingUid(); - final boolean permissionGranted = customTokens || - permissionIsGranted(account, authTokenType, callerUid); - - final Bundle loginOptions = (loginOptionsIn == null) ? new Bundle() : - loginOptionsIn; - // let authenticator know the identity of the caller - loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid); - loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid()); - if (notifyOnAuthFailure) { - loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true); - } - - long identityToken = clearCallingIdentity(); - try { - // if the caller has permission, do the peek. otherwise go the more expensive - // route of starting a Session - if (!customTokens && permissionGranted) { - String authToken = readAuthTokenInternal(accounts, account, authTokenType); - if (authToken != null) { - Bundle result = new Bundle(); - result.putString(AccountManager.KEY_AUTHTOKEN, authToken); - result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); - result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); - onResult(response, result); - return; - } - } - - new Session(accounts, response, account.type, expectActivityLaunch, - false /* stripAuthTokenFromResult */) { - protected String toDebugString(long now) { - if (loginOptions != null) loginOptions.keySet(); - return super.toDebugString(now) + ", getAuthToken" - + ", " + account - + ", authTokenType " + authTokenType - + ", loginOptions " + loginOptions - + ", notifyOnAuthFailure " + notifyOnAuthFailure; - } - - public void run() throws RemoteException { - // If the caller doesn't have permission then create and return the - // "grant permission" intent instead of the "getAuthToken" intent. - if (!permissionGranted) { - mAuthenticator.getAuthTokenLabel(this, authTokenType); - } else { - mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions); - } - } - - public void onResult(Bundle result) { - if (result != null) { - if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) { - Intent intent = newGrantCredentialsPermissionIntent(account, callerUid, - new AccountAuthenticatorResponse(this), - authTokenType, - result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL)); - Bundle bundle = new Bundle(); - bundle.putParcelable(AccountManager.KEY_INTENT, intent); - onResult(bundle); - return; - } - String authToken = result.getString(AccountManager.KEY_AUTHTOKEN); - if (authToken != null) { - String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); - String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE); - if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) { - onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, - "the type and name should not be empty"); - return; - } - if (!customTokens) { - saveAuthTokenToDatabase(mAccounts, new Account(name, type), - authTokenType, authToken); - } - } - - Intent intent = result.getParcelable(AccountManager.KEY_INTENT); - if (intent != null && notifyOnAuthFailure && !customTokens) { - doNotification(mAccounts, - account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE), - intent, accounts.userId); - } - } - super.onResult(result); - } - }.bind(); - } finally { - restoreCallingIdentity(identityToken); - } - } - - private void createNoCredentialsPermissionNotification(Account account, Intent intent, - int userId) { - int uid = intent.getIntExtra( - GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1); - String authTokenType = intent.getStringExtra( - GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE); - String authTokenLabel = intent.getStringExtra( - GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_LABEL); - - Notification n = new Notification(android.R.drawable.stat_sys_warning, null, - 0 /* when */); - final String titleAndSubtitle = - mContext.getString(R.string.permission_request_notification_with_subtitle, - account.name); - final int index = titleAndSubtitle.indexOf('\n'); - String title = titleAndSubtitle; - String subtitle = ""; - if (index > 0) { - title = titleAndSubtitle.substring(0, index); - subtitle = titleAndSubtitle.substring(index + 1); - } - UserHandle user = new UserHandle(userId); - n.setLatestEventInfo(mContext, title, subtitle, - PendingIntent.getActivityAsUser(mContext, 0, intent, - PendingIntent.FLAG_CANCEL_CURRENT, null, user)); - installNotification(getCredentialPermissionNotificationId( - account, authTokenType, uid), n, user); - } - - private Intent newGrantCredentialsPermissionIntent(Account account, int uid, - AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) { - - Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class); - // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag. - // Since it was set in Eclair+ we can't change it without breaking apps using - // the intent from a non-Activity context. - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.addCategory( - String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid))); - - intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account); - intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType); - intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response); - intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid); - - return intent; - } - - private Integer getCredentialPermissionNotificationId(Account account, String authTokenType, - int uid) { - Integer id; - UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); - synchronized (accounts.credentialsPermissionNotificationIds) { - final Pair<Pair<Account, String>, Integer> key = - new Pair<Pair<Account, String>, Integer>( - new Pair<Account, String>(account, authTokenType), uid); - id = accounts.credentialsPermissionNotificationIds.get(key); - if (id == null) { - id = mNotificationIds.incrementAndGet(); - accounts.credentialsPermissionNotificationIds.put(key, id); - } - } - return id; - } - - private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) { - Integer id; - synchronized (accounts.signinRequiredNotificationIds) { - id = accounts.signinRequiredNotificationIds.get(account); - if (id == null) { - id = mNotificationIds.incrementAndGet(); - accounts.signinRequiredNotificationIds.put(account, id); - } - } - return id; - } - - public void addAcount(final IAccountManagerResponse response, final String accountType, - final String authTokenType, final String[] requiredFeatures, - final boolean expectActivityLaunch, final Bundle optionsIn) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "addAccount: accountType " + accountType - + ", response " + response - + ", authTokenType " + authTokenType - + ", requiredFeatures " + stringArrayToString(requiredFeatures) - + ", expectActivityLaunch " + expectActivityLaunch - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (response == null) throw new IllegalArgumentException("response is null"); - if (accountType == null) throw new IllegalArgumentException("accountType is null"); - checkManageAccountsPermission(); - - UserAccounts accounts = getUserAccountsForCaller(); - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn; - options.putInt(AccountManager.KEY_CALLER_UID, uid); - options.putInt(AccountManager.KEY_CALLER_PID, pid); - - long identityToken = clearCallingIdentity(); - try { - new Session(accounts, response, accountType, expectActivityLaunch, - true /* stripAuthTokenFromResult */) { - public void run() throws RemoteException { - mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures, - options); - } - - protected String toDebugString(long now) { - return super.toDebugString(now) + ", addAccount" - + ", accountType " + accountType - + ", requiredFeatures " - + (requiredFeatures != null - ? TextUtils.join(",", requiredFeatures) - : null); - } - }.bind(); - } finally { - restoreCallingIdentity(identityToken); - } - } - - @Override - public void confirmCredentialsAsUser(IAccountManagerResponse response, - final Account account, final Bundle options, final boolean expectActivityLaunch, - int userId) { - // Only allow the system process to read accounts of other users - if (userId != UserHandle.getCallingUserId() - && Binder.getCallingUid() != android.os.Process.myUid()) { - throw new SecurityException("User " + UserHandle.getCallingUserId() - + " trying to confirm account credentials for " + userId); - } - - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "confirmCredentials: " + account - + ", response " + response - + ", expectActivityLaunch " + expectActivityLaunch - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (response == null) throw new IllegalArgumentException("response is null"); - if (account == null) throw new IllegalArgumentException("account is null"); - checkManageAccountsPermission(); - UserAccounts accounts = getUserAccounts(userId); - long identityToken = clearCallingIdentity(); - try { - new Session(accounts, response, account.type, expectActivityLaunch, - true /* stripAuthTokenFromResult */) { - public void run() throws RemoteException { - mAuthenticator.confirmCredentials(this, account, options); - } - protected String toDebugString(long now) { - return super.toDebugString(now) + ", confirmCredentials" - + ", " + account; - } - }.bind(); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void updateCredentials(IAccountManagerResponse response, final Account account, - final String authTokenType, final boolean expectActivityLaunch, - final Bundle loginOptions) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "updateCredentials: " + account - + ", response " + response - + ", authTokenType " + authTokenType - + ", expectActivityLaunch " + expectActivityLaunch - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (response == null) throw new IllegalArgumentException("response is null"); - if (account == null) throw new IllegalArgumentException("account is null"); - if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); - checkManageAccountsPermission(); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - new Session(accounts, response, account.type, expectActivityLaunch, - true /* stripAuthTokenFromResult */) { - public void run() throws RemoteException { - mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions); - } - protected String toDebugString(long now) { - if (loginOptions != null) loginOptions.keySet(); - return super.toDebugString(now) + ", updateCredentials" - + ", " + account - + ", authTokenType " + authTokenType - + ", loginOptions " + loginOptions; - } - }.bind(); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void editProperties(IAccountManagerResponse response, final String accountType, - final boolean expectActivityLaunch) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "editProperties: accountType " + accountType - + ", response " + response - + ", expectActivityLaunch " + expectActivityLaunch - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (response == null) throw new IllegalArgumentException("response is null"); - if (accountType == null) throw new IllegalArgumentException("accountType is null"); - checkManageAccountsPermission(); - UserAccounts accounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - new Session(accounts, response, accountType, expectActivityLaunch, - true /* stripAuthTokenFromResult */) { - public void run() throws RemoteException { - mAuthenticator.editProperties(this, mAccountType); - } - protected String toDebugString(long now) { - return super.toDebugString(now) + ", editProperties" - + ", accountType " + accountType; - } - }.bind(); - } finally { - restoreCallingIdentity(identityToken); - } - } - - private class GetAccountsByTypeAndFeatureSession extends Session { - private final String[] mFeatures; - private volatile Account[] mAccountsOfType = null; - private volatile ArrayList<Account> mAccountsWithFeatures = null; - private volatile int mCurrentAccount = 0; - - public GetAccountsByTypeAndFeatureSession(UserAccounts accounts, - IAccountManagerResponse response, String type, String[] features) { - super(accounts, response, type, false /* expectActivityLaunch */, - true /* stripAuthTokenFromResult */); - mFeatures = features; - } - - public void run() throws RemoteException { - synchronized (mAccounts.cacheLock) { - mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType); - } - // check whether each account matches the requested features - mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length); - mCurrentAccount = 0; - - checkAccount(); - } - - public void checkAccount() { - if (mCurrentAccount >= mAccountsOfType.length) { - sendResult(); - return; - } - - final IAccountAuthenticator accountAuthenticator = mAuthenticator; - if (accountAuthenticator == null) { - // It is possible that the authenticator has died, which is indicated by - // mAuthenticator being set to null. If this happens then just abort. - // There is no need to send back a result or error in this case since - // that already happened when mAuthenticator was cleared. - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "checkAccount: aborting session since we are no longer" - + " connected to the authenticator, " + toDebugString()); - } - return; - } - try { - accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures); - } catch (RemoteException e) { - onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception"); - } - } - - public void onResult(Bundle result) { - mNumResults++; - if (result == null) { - onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle"); - return; - } - if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { - mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]); - } - mCurrentAccount++; - checkAccount(); - } - - public void sendResult() { - IAccountManagerResponse response = getResponseAndClose(); - if (response != null) { - try { - Account[] accounts = new Account[mAccountsWithFeatures.size()]; - for (int i = 0; i < accounts.length; i++) { - accounts[i] = mAccountsWithFeatures.get(i); - } - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response " - + response); - } - Bundle result = new Bundle(); - result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); - response.onResult(result); - } catch (RemoteException e) { - // if the caller is dead then there is no one to care about remote exceptions - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "failure while notifying response", e); - } - } - } - } - - - protected String toDebugString(long now) { - return super.toDebugString(now) + ", getAccountsByTypeAndFeatures" - + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null); - } - } - - /** - * Returns the accounts for a specific user - * @hide - */ - public Account[] getAccounts(int userId) { - checkReadAccountsPermission(); - UserAccounts accounts = getUserAccounts(userId); - long identityToken = clearCallingIdentity(); - try { - synchronized (accounts.cacheLock) { - return getAccountsFromCacheLocked(accounts, null); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - /** - * Returns accounts for all running users. - * - * @hide - */ - public AccountAndUser[] getRunningAccounts() { - final int[] runningUserIds; - try { - runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds(); - } catch (RemoteException e) { - // Running in system_server; should never happen - throw new RuntimeException(e); - } - return getAccounts(runningUserIds); - } - - /** {@hide} */ - public AccountAndUser[] getAllAccounts() { - final List<UserInfo> users = getUserManager().getUsers(); - final int[] userIds = new int[users.size()]; - for (int i = 0; i < userIds.length; i++) { - userIds[i] = users.get(i).id; - } - return getAccounts(userIds); - } - - private AccountAndUser[] getAccounts(int[] userIds) { - final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList(); - synchronized (mUsers) { - for (int userId : userIds) { - UserAccounts userAccounts = getUserAccounts(userId); - if (userAccounts == null) continue; - synchronized (userAccounts.cacheLock) { - Account[] accounts = getAccountsFromCacheLocked(userAccounts, null); - for (int a = 0; a < accounts.length; a++) { - runningAccounts.add(new AccountAndUser(accounts[a], userId)); - } - } - } - } - - AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()]; - return runningAccounts.toArray(accountsArray); - } - - @Override - public Account[] getAccountsAsUser(String type, int userId) { - // Only allow the system process to read accounts of other users - if (userId != UserHandle.getCallingUserId() - && Binder.getCallingUid() != android.os.Process.myUid()) { - throw new SecurityException("User " + UserHandle.getCallingUserId() - + " trying to get account for " + userId); - } - - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "getAccounts: accountType " + type - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - checkReadAccountsPermission(); - UserAccounts accounts = getUserAccounts(userId); - long identityToken = clearCallingIdentity(); - try { - synchronized (accounts.cacheLock) { - return getAccountsFromCacheLocked(accounts, type); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - @Override - public Account[] getAccounts(String type) { - return getAccountsAsUser(type, UserHandle.getCallingUserId()); - } - - public void getAccountsByFeatures(IAccountManagerResponse response, - String type, String[] features) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "getAccounts: accountType " + type - + ", response " + response - + ", features " + stringArrayToString(features) - + ", caller's uid " + Binder.getCallingUid() - + ", pid " + Binder.getCallingPid()); - } - if (response == null) throw new IllegalArgumentException("response is null"); - if (type == null) throw new IllegalArgumentException("accountType is null"); - checkReadAccountsPermission(); - UserAccounts userAccounts = getUserAccountsForCaller(); - long identityToken = clearCallingIdentity(); - try { - if (features == null || features.length == 0) { - Account[] accounts; - synchronized (userAccounts.cacheLock) { - accounts = getAccountsFromCacheLocked(userAccounts, type); - } - Bundle result = new Bundle(); - result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); - onResult(response, result); - return; - } - new GetAccountsByTypeAndFeatureSession(userAccounts, response, type, features).bind(); - } finally { - restoreCallingIdentity(identityToken); - } - } - - private long getAccountIdLocked(SQLiteDatabase db, Account account) { - Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID}, - "name=? AND type=?", new String[]{account.name, account.type}, null, null, null); - try { - if (cursor.moveToNext()) { - return cursor.getLong(0); - } - return -1; - } finally { - cursor.close(); - } - } - - private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) { - Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID}, - EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?", - new String[]{key}, null, null, null); - try { - if (cursor.moveToNext()) { - return cursor.getLong(0); - } - return -1; - } finally { - cursor.close(); - } - } - - private abstract class Session extends IAccountAuthenticatorResponse.Stub - implements IBinder.DeathRecipient, ServiceConnection { - IAccountManagerResponse mResponse; - final String mAccountType; - final boolean mExpectActivityLaunch; - final long mCreationTime; - - public int mNumResults = 0; - private int mNumRequestContinued = 0; - private int mNumErrors = 0; - - - IAccountAuthenticator mAuthenticator = null; - - private final boolean mStripAuthTokenFromResult; - protected final UserAccounts mAccounts; - - public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType, - boolean expectActivityLaunch, boolean stripAuthTokenFromResult) { - super(); - if (response == null) throw new IllegalArgumentException("response is null"); - if (accountType == null) throw new IllegalArgumentException("accountType is null"); - mAccounts = accounts; - mStripAuthTokenFromResult = stripAuthTokenFromResult; - mResponse = response; - mAccountType = accountType; - mExpectActivityLaunch = expectActivityLaunch; - mCreationTime = SystemClock.elapsedRealtime(); - synchronized (mSessions) { - mSessions.put(toString(), this); - } - try { - response.asBinder().linkToDeath(this, 0 /* flags */); - } catch (RemoteException e) { - mResponse = null; - binderDied(); - } - } - - IAccountManagerResponse getResponseAndClose() { - if (mResponse == null) { - // this session has already been closed - return null; - } - IAccountManagerResponse response = mResponse; - close(); // this clears mResponse so we need to save the response before this call - return response; - } - - private void close() { - synchronized (mSessions) { - if (mSessions.remove(toString()) == null) { - // the session was already closed, so bail out now - return; - } - } - if (mResponse != null) { - // stop listening for response deaths - mResponse.asBinder().unlinkToDeath(this, 0 /* flags */); - - // clear this so that we don't accidentally send any further results - mResponse = null; - } - cancelTimeout(); - unbind(); - } - - public void binderDied() { - mResponse = null; - close(); - } - - protected String toDebugString() { - return toDebugString(SystemClock.elapsedRealtime()); - } - - protected String toDebugString(long now) { - return "Session: expectLaunch " + mExpectActivityLaunch - + ", connected " + (mAuthenticator != null) - + ", stats (" + mNumResults + "/" + mNumRequestContinued - + "/" + mNumErrors + ")" - + ", lifetime " + ((now - mCreationTime) / 1000.0); - } - - void bind() { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "initiating bind to authenticator type " + mAccountType); - } - if (!bindToAuthenticator(mAccountType)) { - Log.d(TAG, "bind attempt failed for " + toDebugString()); - onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure"); - } - } - - private void unbind() { - if (mAuthenticator != null) { - mAuthenticator = null; - mContext.unbindService(this); - } - } - - public void scheduleTimeout() { - mMessageHandler.sendMessageDelayed( - mMessageHandler.obtainMessage(MESSAGE_TIMED_OUT, this), TIMEOUT_DELAY_MS); - } - - public void cancelTimeout() { - mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this); - } - - public void onServiceConnected(ComponentName name, IBinder service) { - mAuthenticator = IAccountAuthenticator.Stub.asInterface(service); - try { - run(); - } catch (RemoteException e) { - onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, - "remote exception"); - } - } - - public void onServiceDisconnected(ComponentName name) { - mAuthenticator = null; - IAccountManagerResponse response = getResponseAndClose(); - if (response != null) { - try { - response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, - "disconnected"); - } catch (RemoteException e) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Session.onServiceDisconnected: " - + "caught RemoteException while responding", e); - } - } - } - } - - public abstract void run() throws RemoteException; - - public void onTimedOut() { - IAccountManagerResponse response = getResponseAndClose(); - if (response != null) { - try { - response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, - "timeout"); - } catch (RemoteException e) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding", - e); - } - } - } - } - - public void onResult(Bundle result) { - mNumResults++; - if (result != null && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) { - String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME); - String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE); - if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) { - Account account = new Account(accountName, accountType); - cancelNotification(getSigninRequiredNotificationId(mAccounts, account), - new UserHandle(mAccounts.userId)); - } - } - IAccountManagerResponse response; - if (mExpectActivityLaunch && result != null - && result.containsKey(AccountManager.KEY_INTENT)) { - response = mResponse; - } else { - response = getResponseAndClose(); - } - if (response != null) { - try { - if (result == null) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, getClass().getSimpleName() - + " calling onError() on response " + response); - } - response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, - "null bundle returned"); - } else { - if (mStripAuthTokenFromResult) { - result.remove(AccountManager.KEY_AUTHTOKEN); - } - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, getClass().getSimpleName() - + " calling onResult() on response " + response); - } - response.onResult(result); - } - } catch (RemoteException e) { - // if the caller is dead then there is no one to care about remote exceptions - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "failure while notifying response", e); - } - } - } - } - - public void onRequestContinued() { - mNumRequestContinued++; - } - - public void onError(int errorCode, String errorMessage) { - mNumErrors++; - IAccountManagerResponse response = getResponseAndClose(); - if (response != null) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, getClass().getSimpleName() - + " calling onError() on response " + response); - } - try { - response.onError(errorCode, errorMessage); - } catch (RemoteException e) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Session.onError: caught RemoteException while responding", e); - } - } - } else { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Session.onError: already closed"); - } - } - } - - /** - * find the component name for the authenticator and initiate a bind - * if no authenticator or the bind fails then return false, otherwise return true - */ - private boolean bindToAuthenticator(String authenticatorType) { - final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; - authenticatorInfo = mAuthenticatorCache.getServiceInfo( - AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId); - if (authenticatorInfo == null) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "there is no authenticator for " + authenticatorType - + ", bailing out"); - } - return false; - } - - Intent intent = new Intent(); - intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT); - intent.setComponent(authenticatorInfo.componentName); - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName); - } - if (!mContext.bindService(intent, this, Context.BIND_AUTO_CREATE, mAccounts.userId)) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed"); - } - return false; - } - - - return true; - } - } - - private class MessageHandler extends Handler { - MessageHandler(Looper looper) { - super(looper); - } - - public void handleMessage(Message msg) { - switch (msg.what) { - case MESSAGE_TIMED_OUT: - Session session = (Session)msg.obj; - session.onTimedOut(); - break; - - default: - throw new IllegalStateException("unhandled message: " + msg.what); - } - } - } - - private static String getDatabaseName(int userId) { - File systemDir = Environment.getSystemSecureDirectory(); - File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME); - if (userId == 0) { - // Migrate old file, if it exists, to the new location. - // Make sure the new file doesn't already exist. A dummy file could have been - // accidentally created in the old location, causing the new one to become corrupted - // as well. - File oldFile = new File(systemDir, DATABASE_NAME); - if (oldFile.exists() && !databaseFile.exists()) { - // Check for use directory; create if it doesn't exist, else renameTo will fail - File userDir = Environment.getUserSystemDirectory(userId); - if (!userDir.exists()) { - if (!userDir.mkdirs()) { - throw new IllegalStateException("User dir cannot be created: " + userDir); - } - } - if (!oldFile.renameTo(databaseFile)) { - throw new IllegalStateException("User dir cannot be migrated: " + databaseFile); - } - } - } - return databaseFile.getPath(); - } - - static class DatabaseHelper extends SQLiteOpenHelper { - - public DatabaseHelper(Context context, int userId) { - super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION); - } - - /** - * This call needs to be made while the mCacheLock is held. The way to - * ensure this is to get the lock any time a method is called ont the DatabaseHelper - * @param db The database. - */ - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( " - + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + ACCOUNTS_NAME + " TEXT NOT NULL, " - + ACCOUNTS_TYPE + " TEXT NOT NULL, " - + ACCOUNTS_PASSWORD + " TEXT, " - + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))"); - - db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( " - + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, " - + AUTHTOKENS_TYPE + " TEXT NOT NULL, " - + AUTHTOKENS_AUTHTOKEN + " TEXT, " - + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))"); - - createGrantsTable(db); - - db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( " - + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + EXTRAS_ACCOUNTS_ID + " INTEGER, " - + EXTRAS_KEY + " TEXT NOT NULL, " - + EXTRAS_VALUE + " TEXT, " - + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))"); - - db.execSQL("CREATE TABLE " + TABLE_META + " ( " - + META_KEY + " TEXT PRIMARY KEY NOT NULL, " - + META_VALUE + " TEXT)"); - - createAccountsDeletionTrigger(db); - } - - private void createAccountsDeletionTrigger(SQLiteDatabase db) { - db.execSQL("" - + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS - + " BEGIN" - + " DELETE FROM " + TABLE_AUTHTOKENS - + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" - + " DELETE FROM " + TABLE_EXTRAS - + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" - + " DELETE FROM " + TABLE_GRANTS - + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;" - + " END"); - } - - private void createGrantsTable(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( " - + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, " - + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, " - + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, " - + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE - + "," + GRANTS_GRANTEE_UID + "))"); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion); - - if (oldVersion == 1) { - // no longer need to do anything since the work is done - // when upgrading from version 2 - oldVersion++; - } - - if (oldVersion == 2) { - createGrantsTable(db); - db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete"); - createAccountsDeletionTrigger(db); - oldVersion++; - } - - if (oldVersion == 3) { - db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE + - " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'"); - oldVersion++; - } - } - - @Override - public void onOpen(SQLiteDatabase db) { - if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME); - } - } - - public IBinder onBind(Intent intent) { - return asBinder(); - } - - /** - * Searches array of arguments for the specified string - * @param args array of argument strings - * @param value value to search for - * @return true if the value is contained in the array - */ - private static boolean scanArgs(String[] args, String value) { - if (args != null) { - for (String arg : args) { - if (value.equals(arg)) { - return true; - } - } - } - return false; - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - fout.println("Permission Denial: can't dump AccountsManager from from pid=" - + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() - + " without permission " + android.Manifest.permission.DUMP); - return; - } - final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c"); - final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " "); - - final List<UserInfo> users = getUserManager().getUsers(); - for (UserInfo user : users) { - ipw.println("User " + user + ":"); - ipw.increaseIndent(); - dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest); - ipw.println(); - ipw.decreaseIndent(); - } - } - - private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout, - String[] args, boolean isCheckinRequest) { - synchronized (userAccounts.cacheLock) { - final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase(); - - if (isCheckinRequest) { - // This is a checkin request. *Only* upload the account types and the count of each. - Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION, - null, null, ACCOUNTS_TYPE, null, null); - try { - while (cursor.moveToNext()) { - // print type,count - fout.println(cursor.getString(0) + "," + cursor.getString(1)); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - } else { - Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */); - fout.println("Accounts: " + accounts.length); - for (Account account : accounts) { - fout.println(" " + account); - } - - fout.println(); - synchronized (mSessions) { - final long now = SystemClock.elapsedRealtime(); - fout.println("Active Sessions: " + mSessions.size()); - for (Session session : mSessions.values()) { - fout.println(" " + session.toDebugString(now)); - } - } - - fout.println(); - mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId); - } - } - } - - private void doNotification(UserAccounts accounts, Account account, CharSequence message, - Intent intent, int userId) { - long identityToken = clearCallingIdentity(); - try { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "doNotification: " + message + " intent:" + intent); - } - - if (intent.getComponent() != null && - GrantCredentialsPermissionActivity.class.getName().equals( - intent.getComponent().getClassName())) { - createNoCredentialsPermissionNotification(account, intent, userId); - } else { - final Integer notificationId = getSigninRequiredNotificationId(accounts, account); - intent.addCategory(String.valueOf(notificationId)); - Notification n = new Notification(android.R.drawable.stat_sys_warning, null, - 0 /* when */); - UserHandle user = new UserHandle(userId); - final String notificationTitleFormat = - mContext.getText(R.string.notification_title).toString(); - n.setLatestEventInfo(mContext, - String.format(notificationTitleFormat, account.name), - message, PendingIntent.getActivityAsUser( - mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, - null, user)); - installNotification(notificationId, n, user); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - protected void installNotification(final int notificationId, final Notification n, - UserHandle user) { - ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE)) - .notifyAsUser(null, notificationId, n, user); - } - - protected void cancelNotification(int id, UserHandle user) { - long identityToken = clearCallingIdentity(); - try { - ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE)) - .cancelAsUser(null, id, user); - } finally { - restoreCallingIdentity(identityToken); - } - } - - /** Succeeds if any of the specified permissions are granted. */ - private void checkBinderPermission(String... permissions) { - final int uid = Binder.getCallingUid(); - - for (String perm : permissions) { - if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, " caller uid " + uid + " has " + perm); - } - return; - } - } - - String msg = "caller uid " + uid + " lacks any of " + TextUtils.join(",", permissions); - Log.w(TAG, " " + msg); - throw new SecurityException(msg); - } - - private boolean inSystemImage(int callingUid) { - final int callingUserId = UserHandle.getUserId(callingUid); - - final PackageManager userPackageManager; - try { - userPackageManager = mContext.createPackageContextAsUser( - "android", 0, new UserHandle(callingUserId)).getPackageManager(); - } catch (NameNotFoundException e) { - return false; - } - - String[] packages = userPackageManager.getPackagesForUid(callingUid); - for (String name : packages) { - try { - PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */); - if (packageInfo != null - && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - return true; - } - } catch (PackageManager.NameNotFoundException e) { - return false; - } - } - return false; - } - - private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) { - final boolean inSystemImage = inSystemImage(callerUid); - final boolean fromAuthenticator = account != null - && hasAuthenticatorUid(account.type, callerUid); - final boolean hasExplicitGrants = account != null - && hasExplicitlyGrantedPermission(account, authTokenType, callerUid); - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid " - + callerUid + ", " + account - + ": is authenticator? " + fromAuthenticator - + ", has explicit permission? " + hasExplicitGrants); - } - return fromAuthenticator || hasExplicitGrants || inSystemImage; - } - - private boolean hasAuthenticatorUid(String accountType, int callingUid) { - final int callingUserId = UserHandle.getUserId(callingUid); - for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo : - mAuthenticatorCache.getAllServices(callingUserId)) { - if (serviceInfo.type.type.equals(accountType)) { - return (serviceInfo.uid == callingUid) || - (mPackageManager.checkSignatures(serviceInfo.uid, callingUid) - == PackageManager.SIGNATURE_MATCH); - } - } - return false; - } - - private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType, - int callerUid) { - if (callerUid == android.os.Process.SYSTEM_UID) { - return true; - } - UserAccounts accounts = getUserAccountsForCaller(); - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); - String[] args = { String.valueOf(callerUid), authTokenType, - account.name, account.type}; - final boolean permissionGranted = - DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0; - if (!permissionGranted && ActivityManager.isRunningInTestHarness()) { - // TODO: Skip this check when running automated tests. Replace this - // with a more general solution. - Log.d(TAG, "no credentials permission for usage of " + account + ", " - + authTokenType + " by uid " + callerUid - + " but ignoring since device is in test harness."); - return true; - } - return permissionGranted; - } - } - - private void checkCallingUidAgainstAuthenticator(Account account) { - final int uid = Binder.getCallingUid(); - if (account == null || !hasAuthenticatorUid(account.type, uid)) { - String msg = "caller uid " + uid + " is different than the authenticator's uid"; - Log.w(TAG, msg); - throw new SecurityException(msg); - } - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "caller uid " + uid + " is the same as the authenticator's uid"); - } - } - - private void checkAuthenticateAccountsPermission(Account account) { - checkBinderPermission(Manifest.permission.AUTHENTICATE_ACCOUNTS); - checkCallingUidAgainstAuthenticator(account); - } - - private void checkReadAccountsPermission() { - checkBinderPermission(Manifest.permission.GET_ACCOUNTS); - } - - private void checkManageAccountsPermission() { - checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS); - } - - private void checkManageAccountsOrUseCredentialsPermissions() { - checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS, - Manifest.permission.USE_CREDENTIALS); - } - - public void updateAppPermission(Account account, String authTokenType, int uid, boolean value) - throws RemoteException { - final int callingUid = getCallingUid(); - - if (callingUid != android.os.Process.SYSTEM_UID) { - throw new SecurityException(); - } - - if (value) { - grantAppPermission(account, authTokenType, uid); - } else { - revokeAppPermission(account, authTokenType, uid); - } - } - - /** - * Allow callers with the given uid permission to get credentials for account/authTokenType. - * <p> - * Although this is public it can only be accessed via the AccountManagerService object - * which is in the system. This means we don't need to protect it with permissions. - * @hide - */ - private void grantAppPermission(Account account, String authTokenType, int uid) { - if (account == null || authTokenType == null) { - Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception()); - return; - } - UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - db.beginTransaction(); - try { - long accountId = getAccountIdLocked(db, account); - if (accountId >= 0) { - ContentValues values = new ContentValues(); - values.put(GRANTS_ACCOUNTS_ID, accountId); - values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType); - values.put(GRANTS_GRANTEE_UID, uid); - db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values); - db.setTransactionSuccessful(); - } - } finally { - db.endTransaction(); - } - cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), - new UserHandle(accounts.userId)); - } - } - - /** - * Don't allow callers with the given uid permission to get credentials for - * account/authTokenType. - * <p> - * Although this is public it can only be accessed via the AccountManagerService object - * which is in the system. This means we don't need to protect it with permissions. - * @hide - */ - private void revokeAppPermission(Account account, String authTokenType, int uid) { - if (account == null || authTokenType == null) { - Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception()); - return; - } - UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); - synchronized (accounts.cacheLock) { - final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); - db.beginTransaction(); - try { - long accountId = getAccountIdLocked(db, account); - if (accountId >= 0) { - db.delete(TABLE_GRANTS, - GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND " - + GRANTS_GRANTEE_UID + "=?", - new String[]{String.valueOf(accountId), authTokenType, - String.valueOf(uid)}); - db.setTransactionSuccessful(); - } - } finally { - db.endTransaction(); - } - cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), - new UserHandle(accounts.userId)); - } - } - - static final private String stringArrayToString(String[] value) { - return value != null ? ("[" + TextUtils.join(",", value) + "]") : null; - } - - private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) { - final Account[] oldAccountsForType = accounts.accountCache.get(account.type); - if (oldAccountsForType != null) { - ArrayList<Account> newAccountsList = new ArrayList<Account>(); - for (Account curAccount : oldAccountsForType) { - if (!curAccount.equals(account)) { - newAccountsList.add(curAccount); - } - } - if (newAccountsList.isEmpty()) { - accounts.accountCache.remove(account.type); - } else { - Account[] newAccountsForType = new Account[newAccountsList.size()]; - newAccountsForType = newAccountsList.toArray(newAccountsForType); - accounts.accountCache.put(account.type, newAccountsForType); - } - } - accounts.userDataCache.remove(account); - accounts.authTokenCache.remove(account); - } - - /** - * This assumes that the caller has already checked that the account is not already present. - */ - private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) { - Account[] accountsForType = accounts.accountCache.get(account.type); - int oldLength = (accountsForType != null) ? accountsForType.length : 0; - Account[] newAccountsForType = new Account[oldLength + 1]; - if (accountsForType != null) { - System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength); - } - newAccountsForType[oldLength] = account; - accounts.accountCache.put(account.type, newAccountsForType); - } - - protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType) { - if (accountType != null) { - final Account[] accounts = userAccounts.accountCache.get(accountType); - if (accounts == null) { - return EMPTY_ACCOUNT_ARRAY; - } else { - return Arrays.copyOf(accounts, accounts.length); - } - } else { - int totalLength = 0; - for (Account[] accounts : userAccounts.accountCache.values()) { - totalLength += accounts.length; - } - if (totalLength == 0) { - return EMPTY_ACCOUNT_ARRAY; - } - Account[] accounts = new Account[totalLength]; - totalLength = 0; - for (Account[] accountsOfType : userAccounts.accountCache.values()) { - System.arraycopy(accountsOfType, 0, accounts, totalLength, - accountsOfType.length); - totalLength += accountsOfType.length; - } - return accounts; - } - } - - protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, - Account account, String key, String value) { - HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account); - if (userDataForAccount == null) { - userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); - accounts.userDataCache.put(account, userDataForAccount); - } - if (value == null) { - userDataForAccount.remove(key); - } else { - userDataForAccount.put(key, value); - } - } - - protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db, - Account account, String key, String value) { - HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account); - if (authTokensForAccount == null) { - authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); - accounts.authTokenCache.put(account, authTokensForAccount); - } - if (value == null) { - authTokensForAccount.remove(key); - } else { - authTokensForAccount.put(key, value); - } - } - - protected String readAuthTokenInternal(UserAccounts accounts, Account account, - String authTokenType) { - synchronized (accounts.cacheLock) { - HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account); - if (authTokensForAccount == null) { - // need to populate the cache for this account - final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); - authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); - accounts.authTokenCache.put(account, authTokensForAccount); - } - return authTokensForAccount.get(authTokenType); - } - } - - protected String readUserDataInternal(UserAccounts accounts, Account account, String key) { - synchronized (accounts.cacheLock) { - HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account); - if (userDataForAccount == null) { - // need to populate the cache for this account - final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); - userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); - accounts.userDataCache.put(account, userDataForAccount); - } - return userDataForAccount.get(key); - } - } - - protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked( - final SQLiteDatabase db, Account account) { - HashMap<String, String> userDataForAccount = new HashMap<String, String>(); - Cursor cursor = db.query(TABLE_EXTRAS, - COLUMNS_EXTRAS_KEY_AND_VALUE, - SELECTION_USERDATA_BY_ACCOUNT, - new String[]{account.name, account.type}, - null, null, null); - try { - while (cursor.moveToNext()) { - final String tmpkey = cursor.getString(0); - final String value = cursor.getString(1); - userDataForAccount.put(tmpkey, value); - } - } finally { - cursor.close(); - } - return userDataForAccount; - } - - protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked( - final SQLiteDatabase db, Account account) { - HashMap<String, String> authTokensForAccount = new HashMap<String, String>(); - Cursor cursor = db.query(TABLE_AUTHTOKENS, - COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN, - SELECTION_AUTHTOKENS_BY_ACCOUNT, - new String[]{account.name, account.type}, - null, null, null); - try { - while (cursor.moveToNext()) { - final String type = cursor.getString(0); - final String authToken = cursor.getString(1); - authTokensForAccount.put(type, authToken); - } - } finally { - cursor.close(); - } - return authTokensForAccount; - } -} diff --git a/core/java/android/accounts/IAccountAuthenticatorCache.java b/core/java/android/accounts/IAccountAuthenticatorCache.java deleted file mode 100644 index 06c2106..0000000 --- a/core/java/android/accounts/IAccountAuthenticatorCache.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.accounts; - -import android.content.pm.RegisteredServicesCache; -import android.content.pm.RegisteredServicesCacheListener; -import android.os.Handler; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.Collection; - -/** - * An interface to the Authenticator specialization of RegisteredServicesCache. The use of - * this interface by the AccountManagerService makes it easier to unit test it. - * @hide - */ -public interface IAccountAuthenticatorCache { - /** - * Accessor for the {@link android.content.pm.RegisteredServicesCache.ServiceInfo} that - * matched the specified {@link android.accounts.AuthenticatorDescription} or null - * if none match. - * @param type the authenticator type to return - * @return the {@link android.content.pm.RegisteredServicesCache.ServiceInfo} that - * matches the account type or null if none is present - */ - RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> getServiceInfo( - AuthenticatorDescription type, int userId); - - /** - * @return A copy of a Collection of all the current Authenticators. - */ - Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> getAllServices( - int userId); - - /** - * Dumps the state of the cache. See - * {@link android.os.Binder#dump(java.io.FileDescriptor, java.io.PrintWriter, String[])} - */ - void dump(FileDescriptor fd, PrintWriter fout, String[] args, int userId); - - /** - * Sets a listener that will be notified whenever the authenticator set changes - * @param listener the listener to notify, or null - * @param handler the {@link Handler} on which the notification will be posted. If null - * the notification will be posted on the main thread. - */ - void setListener(RegisteredServicesCacheListener<AuthenticatorDescription> listener, - Handler handler); - - void invalidateCache(int userId); -} diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index b20cf88..e34c827 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -116,6 +116,10 @@ public abstract class ContentResolver { */ public static final String SYNC_EXTRAS_INITIALIZE = "initialize"; + /** @hide */ + public static final Intent ACTION_SYNC_CONN_STATUS_CHANGED = + new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED"); + public static final String SCHEME_CONTENT = "content"; public static final String SCHEME_ANDROID_RESOURCE = "android.resource"; public static final String SCHEME_FILE = "file"; @@ -181,7 +185,7 @@ public abstract class ContentResolver { }; /** @hide */ - static String syncErrorToString(int error) { + public static String syncErrorToString(int error) { if (error < 1 || error > SYNC_ERROR_NAMES.length) { return String.valueOf(error); } diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java deleted file mode 100644 index 8bac888..0000000 --- a/core/java/android/content/ContentService.java +++ /dev/null @@ -1,840 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content; - -import android.accounts.Account; -import android.app.ActivityManager; -import android.database.IContentObserver; -import android.database.sqlite.SQLiteException; -import android.net.Uri; -import android.os.Binder; -import android.os.Bundle; -import android.os.IBinder; -import android.os.Parcel; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.UserHandle; -import android.util.Log; -import android.util.SparseIntArray; -import android.Manifest; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.security.InvalidParameterException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -/** - * {@hide} - */ -public final class ContentService extends IContentService.Stub { - private static final String TAG = "ContentService"; - private Context mContext; - private boolean mFactoryTest; - private final ObserverNode mRootNode = new ObserverNode(""); - private SyncManager mSyncManager = null; - private final Object mSyncManagerLock = new Object(); - - private SyncManager getSyncManager() { - synchronized(mSyncManagerLock) { - try { - // Try to create the SyncManager, return null if it fails (e.g. the disk is full). - if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest); - } catch (SQLiteException e) { - Log.e(TAG, "Can't create SyncManager", e); - } - return mSyncManager; - } - } - - @Override - protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP, - "caller doesn't have the DUMP permission"); - - // This makes it so that future permission checks will be in the context of this - // process rather than the caller's process. We will restore this before returning. - long identityToken = clearCallingIdentity(); - try { - if (mSyncManager == null) { - pw.println("No SyncManager created! (Disk full?)"); - } else { - mSyncManager.dump(fd, pw); - } - pw.println(); - pw.println("Observer tree:"); - synchronized (mRootNode) { - int[] counts = new int[2]; - final SparseIntArray pidCounts = new SparseIntArray(); - mRootNode.dumpLocked(fd, pw, args, "", " ", counts, pidCounts); - pw.println(); - ArrayList<Integer> sorted = new ArrayList<Integer>(); - for (int i=0; i<pidCounts.size(); i++) { - sorted.add(pidCounts.keyAt(i)); - } - Collections.sort(sorted, new Comparator<Integer>() { - @Override - public int compare(Integer lhs, Integer rhs) { - int lc = pidCounts.get(lhs); - int rc = pidCounts.get(rhs); - if (lc < rc) { - return 1; - } else if (lc > rc) { - return -1; - } - return 0; - } - - }); - for (int i=0; i<sorted.size(); i++) { - int pid = sorted.get(i); - pw.print(" pid "); pw.print(pid); pw.print(": "); - pw.print(pidCounts.get(pid)); pw.println(" observers"); - } - pw.println(); - pw.print(" Total number of nodes: "); pw.println(counts[0]); - pw.print(" Total number of observers: "); pw.println(counts[1]); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - @Override - public boolean onTransact(int code, Parcel data, Parcel reply, int flags) - throws RemoteException { - try { - return super.onTransact(code, data, reply, flags); - } catch (RuntimeException e) { - // The content service only throws security exceptions, so let's - // log all others. - if (!(e instanceof SecurityException)) { - Log.e(TAG, "Content Service Crash", e); - } - throw e; - } - } - - /*package*/ ContentService(Context context, boolean factoryTest) { - mContext = context; - mFactoryTest = factoryTest; - } - - public void systemReady() { - getSyncManager(); - } - - /** - * Register a content observer tied to a specific user's view of the provider. - * @param userHandle the user whose view of the provider is to be observed. May be - * the calling user without requiring any permission, otherwise the caller needs to - * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and - * USER_CURRENT are properly handled; all other pseudousers are forbidden. - */ - @Override - public void registerContentObserver(Uri uri, boolean notifyForDescendants, - IContentObserver observer, int userHandle) { - if (observer == null || uri == null) { - throw new IllegalArgumentException("You must pass a valid uri and observer"); - } - - final int callingUser = UserHandle.getCallingUserId(); - if (callingUser != userHandle) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, - "no permission to observe other users' provider view"); - } - - if (userHandle < 0) { - if (userHandle == UserHandle.USER_CURRENT) { - userHandle = ActivityManager.getCurrentUser(); - } else if (userHandle != UserHandle.USER_ALL) { - throw new InvalidParameterException("Bad user handle for registerContentObserver: " - + userHandle); - } - } - - synchronized (mRootNode) { - mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode, - Binder.getCallingUid(), Binder.getCallingPid(), userHandle); - if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri + - " with notifyForDescendants " + notifyForDescendants); - } - } - - public void registerContentObserver(Uri uri, boolean notifyForDescendants, - IContentObserver observer) { - registerContentObserver(uri, notifyForDescendants, observer, - UserHandle.getCallingUserId()); - } - - public void unregisterContentObserver(IContentObserver observer) { - if (observer == null) { - throw new IllegalArgumentException("You must pass a valid observer"); - } - synchronized (mRootNode) { - mRootNode.removeObserverLocked(observer); - if (false) Log.v(TAG, "Unregistered observer " + observer); - } - } - - /** - * Notify observers of a particular user's view of the provider. - * @param userHandle the user whose view of the provider is to be notified. May be - * the calling user without requiring any permission, otherwise the caller needs to - * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and - * USER_CURRENT are properly interpreted; no other pseudousers are allowed. - */ - @Override - public void notifyChange(Uri uri, IContentObserver observer, - boolean observerWantsSelfNotifications, boolean syncToNetwork, - int userHandle) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle - + " from observer " + observer + ", syncToNetwork " + syncToNetwork); - } - - // Notify for any user other than the caller's own requires permission. - final int callingUserHandle = UserHandle.getCallingUserId(); - if (userHandle != callingUserHandle) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, - "no permission to notify other users"); - } - - // We passed the permission check; resolve pseudouser targets as appropriate - if (userHandle < 0) { - if (userHandle == UserHandle.USER_CURRENT) { - userHandle = ActivityManager.getCurrentUser(); - } else if (userHandle != UserHandle.USER_ALL) { - throw new InvalidParameterException("Bad user handle for notifyChange: " - + userHandle); - } - } - - final int uid = Binder.getCallingUid(); - // This makes it so that future permission checks will be in the context of this - // process rather than the caller's process. We will restore this before returning. - long identityToken = clearCallingIdentity(); - try { - ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); - synchronized (mRootNode) { - mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications, - userHandle, calls); - } - final int numCalls = calls.size(); - for (int i=0; i<numCalls; i++) { - ObserverCall oc = calls.get(i); - try { - oc.mObserver.onChange(oc.mSelfChange, uri); - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri); - } - } catch (RemoteException ex) { - synchronized (mRootNode) { - Log.w(TAG, "Found dead observer, removing"); - IBinder binder = oc.mObserver.asBinder(); - final ArrayList<ObserverNode.ObserverEntry> list - = oc.mNode.mObservers; - int numList = list.size(); - for (int j=0; j<numList; j++) { - ObserverNode.ObserverEntry oe = list.get(j); - if (oe.observer.asBinder() == binder) { - list.remove(j); - j--; - numList--; - } - } - } - } - } - if (syncToNetwork) { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid, - uri.getAuthority()); - } - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void notifyChange(Uri uri, IContentObserver observer, - boolean observerWantsSelfNotifications, boolean syncToNetwork) { - notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork, - UserHandle.getCallingUserId()); - } - - /** - * Hide this class since it is not part of api, - * but current unittest framework requires it to be public - * @hide - * - */ - public static final class ObserverCall { - final ObserverNode mNode; - final IContentObserver mObserver; - final boolean mSelfChange; - - ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange) { - mNode = node; - mObserver = observer; - mSelfChange = selfChange; - } - } - - public void requestSync(Account account, String authority, Bundle extras) { - ContentResolver.validateSyncExtrasBundle(extras); - int userId = UserHandle.getCallingUserId(); - int uId = Binder.getCallingUid(); - - // This makes it so that future permission checks will be in the context of this - // process rather than the caller's process. We will restore this before returning. - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - syncManager.scheduleSync(account, userId, uId, authority, extras, 0 /* no delay */, - false /* onlyThoseWithUnkownSyncableState */); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - /** - * Clear all scheduled sync operations that match the uri and cancel the active sync - * if they match the authority and account, if they are present. - * @param account filter the pending and active syncs to cancel using this account - * @param authority filter the pending and active syncs to cancel using this authority - */ - public void cancelSync(Account account, String authority) { - int userId = UserHandle.getCallingUserId(); - - // This makes it so that future permission checks will be in the context of this - // process rather than the caller's process. We will restore this before returning. - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - syncManager.clearScheduledSyncOperations(account, userId, authority); - syncManager.cancelActiveSync(account, userId, authority); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - /** - * Get information about the SyncAdapters that are known to the system. - * @return an array of SyncAdapters that have registered with the system - */ - public SyncAdapterType[] getSyncAdapterTypes() { - // This makes it so that future permission checks will be in the context of this - // process rather than the caller's process. We will restore this before returning. - final int userId = UserHandle.getCallingUserId(); - final long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - return syncManager.getSyncAdapterTypes(userId); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public boolean getSyncAutomatically(Account account, String providerName) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, - "no permission to read the sync settings"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - return syncManager.getSyncStorageEngine().getSyncAutomatically( - account, userId, providerName); - } - } finally { - restoreCallingIdentity(identityToken); - } - return false; - } - - public void setSyncAutomatically(Account account, String providerName, boolean sync) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, - "no permission to write the sync settings"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - syncManager.getSyncStorageEngine().setSyncAutomatically( - account, userId, providerName, sync); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void addPeriodicSync(Account account, String authority, Bundle extras, - long pollFrequency) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, - "no permission to write the sync settings"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - getSyncManager().getSyncStorageEngine().addPeriodicSync( - account, userId, authority, extras, pollFrequency); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void removePeriodicSync(Account account, String authority, Bundle extras) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, - "no permission to write the sync settings"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - getSyncManager().getSyncStorageEngine().removePeriodicSync(account, userId, authority, - extras); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, - "no permission to read the sync settings"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - return getSyncManager().getSyncStorageEngine().getPeriodicSyncs( - account, userId, providerName); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public int getIsSyncable(Account account, String providerName) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, - "no permission to read the sync settings"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - return syncManager.getSyncStorageEngine().getIsSyncable( - account, userId, providerName); - } - } finally { - restoreCallingIdentity(identityToken); - } - return -1; - } - - public void setIsSyncable(Account account, String providerName, int syncable) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, - "no permission to write the sync settings"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - syncManager.getSyncStorageEngine().setIsSyncable( - account, userId, providerName, syncable); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - public boolean getMasterSyncAutomatically() { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, - "no permission to read the sync settings"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId); - } - } finally { - restoreCallingIdentity(identityToken); - } - return false; - } - - public void setMasterSyncAutomatically(boolean flag) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, - "no permission to write the sync settings"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - public boolean isSyncActive(Account account, String authority) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, - "no permission to read the sync stats"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - return syncManager.getSyncStorageEngine().isSyncActive( - account, userId, authority); - } - } finally { - restoreCallingIdentity(identityToken); - } - return false; - } - - public List<SyncInfo> getCurrentSyncs() { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, - "no permission to read the sync stats"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - return getSyncManager().getSyncStorageEngine().getCurrentSyncs(userId); - } finally { - restoreCallingIdentity(identityToken); - } - } - - public SyncStatusInfo getSyncStatus(Account account, String authority) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, - "no permission to read the sync stats"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - return syncManager.getSyncStorageEngine().getStatusByAccountAndAuthority( - account, userId, authority); - } - } finally { - restoreCallingIdentity(identityToken); - } - return null; - } - - public boolean isSyncPending(Account account, String authority) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, - "no permission to read the sync stats"); - int userId = UserHandle.getCallingUserId(); - - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - return syncManager.getSyncStorageEngine().isSyncPending(account, userId, authority); - } - } finally { - restoreCallingIdentity(identityToken); - } - return false; - } - - public void addStatusChangeListener(int mask, ISyncStatusObserver callback) { - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null && callback != null) { - syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - public void removeStatusChangeListener(ISyncStatusObserver callback) { - long identityToken = clearCallingIdentity(); - try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null && callback != null) { - syncManager.getSyncStorageEngine().removeStatusChangeListener(callback); - } - } finally { - restoreCallingIdentity(identityToken); - } - } - - public static ContentService main(Context context, boolean factoryTest) { - ContentService service = new ContentService(context, factoryTest); - ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service); - return service; - } - - /** - * Hide this class since it is not part of api, - * but current unittest framework requires it to be public - * @hide - */ - public static final class ObserverNode { - private class ObserverEntry implements IBinder.DeathRecipient { - public final IContentObserver observer; - public final int uid; - public final int pid; - public final boolean notifyForDescendants; - private final int userHandle; - private final Object observersLock; - - public ObserverEntry(IContentObserver o, boolean n, Object observersLock, - int _uid, int _pid, int _userHandle) { - this.observersLock = observersLock; - observer = o; - uid = _uid; - pid = _pid; - userHandle = _userHandle; - notifyForDescendants = n; - try { - observer.asBinder().linkToDeath(this, 0); - } catch (RemoteException e) { - binderDied(); - } - } - - public void binderDied() { - synchronized (observersLock) { - removeObserverLocked(observer); - } - } - - public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, - String name, String prefix, SparseIntArray pidCounts) { - pidCounts.put(pid, pidCounts.get(pid)+1); - pw.print(prefix); pw.print(name); pw.print(": pid="); - pw.print(pid); pw.print(" uid="); - pw.print(uid); pw.print(" user="); - pw.print(userHandle); pw.print(" target="); - pw.println(Integer.toHexString(System.identityHashCode( - observer != null ? observer.asBinder() : null))); - } - } - - public static final int INSERT_TYPE = 0; - public static final int UPDATE_TYPE = 1; - public static final int DELETE_TYPE = 2; - - private String mName; - private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>(); - private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>(); - - public ObserverNode(String name) { - mName = name; - } - - public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, - String name, String prefix, int[] counts, SparseIntArray pidCounts) { - String innerName = null; - if (mObservers.size() > 0) { - if ("".equals(name)) { - innerName = mName; - } else { - innerName = name + "/" + mName; - } - for (int i=0; i<mObservers.size(); i++) { - counts[1]++; - mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix, - pidCounts); - } - } - if (mChildren.size() > 0) { - if (innerName == null) { - if ("".equals(name)) { - innerName = mName; - } else { - innerName = name + "/" + mName; - } - } - for (int i=0; i<mChildren.size(); i++) { - counts[0]++; - mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix, - counts, pidCounts); - } - } - } - - private String getUriSegment(Uri uri, int index) { - if (uri != null) { - if (index == 0) { - return uri.getAuthority(); - } else { - return uri.getPathSegments().get(index - 1); - } - } else { - return null; - } - } - - private int countUriSegments(Uri uri) { - if (uri == null) { - return 0; - } - return uri.getPathSegments().size() + 1; - } - - // Invariant: userHandle is either a hard user number or is USER_ALL - public void addObserverLocked(Uri uri, IContentObserver observer, - boolean notifyForDescendants, Object observersLock, - int uid, int pid, int userHandle) { - addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock, - uid, pid, userHandle); - } - - private void addObserverLocked(Uri uri, int index, IContentObserver observer, - boolean notifyForDescendants, Object observersLock, - int uid, int pid, int userHandle) { - // If this is the leaf node add the observer - if (index == countUriSegments(uri)) { - mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock, - uid, pid, userHandle)); - return; - } - - // Look to see if the proper child already exists - String segment = getUriSegment(uri, index); - if (segment == null) { - throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer"); - } - int N = mChildren.size(); - for (int i = 0; i < N; i++) { - ObserverNode node = mChildren.get(i); - if (node.mName.equals(segment)) { - node.addObserverLocked(uri, index + 1, observer, notifyForDescendants, - observersLock, uid, pid, userHandle); - return; - } - } - - // No child found, create one - ObserverNode node = new ObserverNode(segment); - mChildren.add(node); - node.addObserverLocked(uri, index + 1, observer, notifyForDescendants, - observersLock, uid, pid, userHandle); - } - - public boolean removeObserverLocked(IContentObserver observer) { - int size = mChildren.size(); - for (int i = 0; i < size; i++) { - boolean empty = mChildren.get(i).removeObserverLocked(observer); - if (empty) { - mChildren.remove(i); - i--; - size--; - } - } - - IBinder observerBinder = observer.asBinder(); - size = mObservers.size(); - for (int i = 0; i < size; i++) { - ObserverEntry entry = mObservers.get(i); - if (entry.observer.asBinder() == observerBinder) { - mObservers.remove(i); - // We no longer need to listen for death notifications. Remove it. - observerBinder.unlinkToDeath(entry, 0); - break; - } - } - - if (mChildren.size() == 0 && mObservers.size() == 0) { - return true; - } - return false; - } - - private void collectMyObserversLocked(boolean leaf, IContentObserver observer, - boolean observerWantsSelfNotifications, int targetUserHandle, - ArrayList<ObserverCall> calls) { - int N = mObservers.size(); - IBinder observerBinder = observer == null ? null : observer.asBinder(); - for (int i = 0; i < N; i++) { - ObserverEntry entry = mObservers.get(i); - - // Don't notify the observer if it sent the notification and isn't interested - // in self notifications - boolean selfChange = (entry.observer.asBinder() == observerBinder); - if (selfChange && !observerWantsSelfNotifications) { - continue; - } - - // Does this observer match the target user? - if (targetUserHandle == UserHandle.USER_ALL - || entry.userHandle == UserHandle.USER_ALL - || targetUserHandle == entry.userHandle) { - // Make sure the observer is interested in the notification - if (leaf || (!leaf && entry.notifyForDescendants)) { - calls.add(new ObserverCall(this, entry.observer, selfChange)); - } - } - } - } - - /** - * targetUserHandle is either a hard user handle or is USER_ALL - */ - public void collectObserversLocked(Uri uri, int index, IContentObserver observer, - boolean observerWantsSelfNotifications, int targetUserHandle, - ArrayList<ObserverCall> calls) { - String segment = null; - int segmentCount = countUriSegments(uri); - if (index >= segmentCount) { - // This is the leaf node, notify all observers - collectMyObserversLocked(true, observer, observerWantsSelfNotifications, - targetUserHandle, calls); - } else if (index < segmentCount){ - segment = getUriSegment(uri, index); - // Notify any observers at this level who are interested in descendants - collectMyObserversLocked(false, observer, observerWantsSelfNotifications, - targetUserHandle, calls); - } - - int N = mChildren.size(); - for (int i = 0; i < N; i++) { - ObserverNode node = mChildren.get(i); - if (segment == null || node.mName.equals(segment)) { - // We found the child, - node.collectObserversLocked(uri, index + 1, - observer, observerWantsSelfNotifications, targetUserHandle, calls); - if (segment != null) { - break; - } - } - } - } - } -} diff --git a/core/java/android/content/PeriodicSync.java b/core/java/android/content/PeriodicSync.java index 17813ec..513a556 100644 --- a/core/java/android/content/PeriodicSync.java +++ b/core/java/android/content/PeriodicSync.java @@ -79,6 +79,25 @@ public class PeriodicSync implements Parcelable { return account.equals(other.account) && authority.equals(other.authority) && period == other.period - && SyncStorageEngine.equals(extras, other.extras); + && syncExtrasEquals(extras, other.extras); + } + + /** {@hide} */ + public static boolean syncExtrasEquals(Bundle b1, Bundle b2) { + if (b1.size() != b2.size()) { + return false; + } + if (b1.isEmpty()) { + return true; + } + for (String key : b1.keySet()) { + if (!b2.containsKey(key)) { + return false; + } + if (!b1.get(key).equals(b2.get(key))) { + return false; + } + } + return true; } } diff --git a/core/java/android/content/SyncAdaptersCache.java b/core/java/android/content/SyncAdaptersCache.java index 7b643a0..8bb3ee7 100644 --- a/core/java/android/content/SyncAdaptersCache.java +++ b/core/java/android/content/SyncAdaptersCache.java @@ -31,7 +31,7 @@ import java.io.IOException; * A cache of services that export the {@link android.content.ISyncAdapter} interface. * @hide */ -/* package private */ class SyncAdaptersCache extends RegisteredServicesCache<SyncAdapterType> { +public class SyncAdaptersCache extends RegisteredServicesCache<SyncAdapterType> { private static final String TAG = "Account"; private static final String SERVICE_INTERFACE = "android.content.SyncAdapter"; @@ -39,7 +39,7 @@ import java.io.IOException; private static final String ATTRIBUTES_NAME = "sync-adapter"; private static final MySerializer sSerializer = new MySerializer(); - SyncAdaptersCache(Context context) { + public SyncAdaptersCache(Context context) { super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, sSerializer); } diff --git a/core/java/android/content/SyncInfo.java b/core/java/android/content/SyncInfo.java index abfe964..0284882 100644 --- a/core/java/android/content/SyncInfo.java +++ b/core/java/android/content/SyncInfo.java @@ -46,7 +46,7 @@ public class SyncInfo implements Parcelable { public final long startTime; /** @hide */ - SyncInfo(int authorityId, Account account, String authority, + public SyncInfo(int authorityId, Account account, String authority, long startTime) { this.authorityId = authorityId; this.account = account; diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java deleted file mode 100644 index e428968..0000000 --- a/core/java/android/content/SyncManager.java +++ /dev/null @@ -1,2771 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content; - -import android.accounts.Account; -import android.accounts.AccountAndUser; -import android.accounts.AccountManager; -import android.accounts.AccountManagerService; -import android.app.ActivityManager; -import android.app.AlarmManager; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.SyncStorageEngine.OnSyncRequestListener; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.ProviderInfo; -import android.content.pm.RegisteredServicesCache; -import android.content.pm.RegisteredServicesCacheListener; -import android.content.pm.ResolveInfo; -import android.content.pm.UserInfo; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.PowerManager; -import android.os.Process; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.os.UserHandle; -import android.os.UserManager; -import android.os.WorkSource; -import android.provider.Settings; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.EventLog; -import android.util.Log; -import android.util.Pair; -import android.util.Slog; - -import com.android.internal.R; -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.IndentingPrintWriter; -import com.google.android.collect.Lists; -import com.google.android.collect.Maps; -import com.google.android.collect.Sets; - -import java.io.FileDescriptor; -import java.io.PrintStream; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.CountDownLatch; - -/** - * @hide - */ -public class SyncManager { - private static final String TAG = "SyncManager"; - - /** Delay a sync due to local changes this long. In milliseconds */ - private static final long LOCAL_SYNC_DELAY; - - /** - * If a sync takes longer than this and the sync queue is not empty then we will - * cancel it and add it back to the end of the sync queue. In milliseconds. - */ - private static final long MAX_TIME_PER_SYNC; - - static { - final boolean isLargeRAM = ActivityManager.isLargeRAM(); - int defaultMaxInitSyncs = isLargeRAM ? 5 : 2; - int defaultMaxRegularSyncs = isLargeRAM ? 2 : 1; - MAX_SIMULTANEOUS_INITIALIZATION_SYNCS = - SystemProperties.getInt("sync.max_init_syncs", defaultMaxInitSyncs); - MAX_SIMULTANEOUS_REGULAR_SYNCS = - SystemProperties.getInt("sync.max_regular_syncs", defaultMaxRegularSyncs); - LOCAL_SYNC_DELAY = - SystemProperties.getLong("sync.local_sync_delay", 30 * 1000 /* 30 seconds */); - MAX_TIME_PER_SYNC = - SystemProperties.getLong("sync.max_time_per_sync", 5 * 60 * 1000 /* 5 minutes */); - SYNC_NOTIFICATION_DELAY = - SystemProperties.getLong("sync.notification_delay", 30 * 1000 /* 30 seconds */); - } - - private static final long SYNC_NOTIFICATION_DELAY; - - /** - * When retrying a sync for the first time use this delay. After that - * the retry time will double until it reached MAX_SYNC_RETRY_TIME. - * In milliseconds. - */ - private static final long INITIAL_SYNC_RETRY_TIME_IN_MS = 30 * 1000; // 30 seconds - - /** - * Default the max sync retry time to this value. - */ - private static final long DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS = 60 * 60; // one hour - - /** - * How long to wait before retrying a sync that failed due to one already being in progress. - */ - private static final int DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS = 10; - - private static final int INITIALIZATION_UNBIND_DELAY_MS = 5000; - - private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*"; - private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm"; - private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock"; - - private static final int MAX_SIMULTANEOUS_REGULAR_SYNCS; - private static final int MAX_SIMULTANEOUS_INITIALIZATION_SYNCS; - - private Context mContext; - - private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0]; - - // TODO: add better locking around mRunningAccounts - private volatile AccountAndUser[] mRunningAccounts = INITIAL_ACCOUNTS_ARRAY; - - volatile private PowerManager.WakeLock mHandleAlarmWakeLock; - volatile private PowerManager.WakeLock mSyncManagerWakeLock; - volatile private boolean mDataConnectionIsConnected = false; - volatile private boolean mStorageIsLow = false; - - private final NotificationManager mNotificationMgr; - private AlarmManager mAlarmService = null; - - private SyncStorageEngine mSyncStorageEngine; - - @GuardedBy("mSyncQueue") - private final SyncQueue mSyncQueue; - - protected final ArrayList<ActiveSyncContext> mActiveSyncContexts = Lists.newArrayList(); - - // set if the sync active indicator should be reported - private boolean mNeedSyncActiveNotification = false; - - private final PendingIntent mSyncAlarmIntent; - // Synchronized on "this". Instead of using this directly one should instead call - // its accessor, getConnManager(). - private ConnectivityManager mConnManagerDoNotUseDirectly; - - protected SyncAdaptersCache mSyncAdapters; - - private BroadcastReceiver mStorageIntentReceiver = - new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Internal storage is low."); - } - mStorageIsLow = true; - cancelActiveSync(null /* any account */, UserHandle.USER_ALL, - null /* any authority */); - } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Internal storage is ok."); - } - mStorageIsLow = false; - sendCheckAlarmsMessage(); - } - } - }; - - private BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - mSyncHandler.onBootCompleted(); - } - }; - - private BroadcastReceiver mBackgroundDataSettingChanged = new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - if (getConnectivityManager().getBackgroundDataSetting()) { - scheduleSync(null /* account */, UserHandle.USER_ALL, - SyncOperation.REASON_BACKGROUND_DATA_SETTINGS_CHANGED, - null /* authority */, - new Bundle(), 0 /* delay */, - false /* onlyThoseWithUnknownSyncableState */); - } - } - }; - - private BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - updateRunningAccounts(); - - // Kick off sync for everyone, since this was a radical account change - scheduleSync(null, UserHandle.USER_ALL, SyncOperation.REASON_ACCOUNTS_UPDATED, null, - null, 0 /* no delay */, false); - } - }; - - private final PowerManager mPowerManager; - - // Use this as a random offset to seed all periodic syncs - private int mSyncRandomOffsetMillis; - - private final UserManager mUserManager; - - private static final long SYNC_ALARM_TIMEOUT_MIN = 30 * 1000; // 30 seconds - private static final long SYNC_ALARM_TIMEOUT_MAX = 2 * 60 * 60 * 1000; // two hours - - private List<UserInfo> getAllUsers() { - return mUserManager.getUsers(); - } - - private boolean containsAccountAndUser(AccountAndUser[] accounts, Account account, int userId) { - boolean found = false; - for (int i = 0; i < accounts.length; i++) { - if (accounts[i].userId == userId - && accounts[i].account.equals(account)) { - found = true; - break; - } - } - return found; - } - - public void updateRunningAccounts() { - mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts(); - - if (mBootCompleted) { - doDatabaseCleanup(); - } - - for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) { - if (!containsAccountAndUser(mRunningAccounts, - currentSyncContext.mSyncOperation.account, - currentSyncContext.mSyncOperation.userId)) { - Log.d(TAG, "canceling sync since the account is no longer running"); - sendSyncFinishedOrCanceledMessage(currentSyncContext, - null /* no result since this is a cancel */); - } - } - - // we must do this since we don't bother scheduling alarms when - // the accounts are not set yet - sendCheckAlarmsMessage(); - } - - private void doDatabaseCleanup() { - for (UserInfo user : mUserManager.getUsers(true)) { - // Skip any partially created/removed users - if (user.partial) continue; - Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(user.id); - mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id); - } - } - - private BroadcastReceiver mConnectivityIntentReceiver = - new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - final boolean wasConnected = mDataConnectionIsConnected; - - // don't use the intent to figure out if network is connected, just check - // ConnectivityManager directly. - mDataConnectionIsConnected = readDataConnectionState(); - if (mDataConnectionIsConnected) { - if (!wasConnected) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Reconnection detected: clearing all backoffs"); - } - mSyncStorageEngine.clearAllBackoffs(mSyncQueue); - } - sendCheckAlarmsMessage(); - } - } - }; - - private boolean readDataConnectionState() { - NetworkInfo networkInfo = getConnectivityManager().getActiveNetworkInfo(); - return (networkInfo != null) && networkInfo.isConnected(); - } - - private BroadcastReceiver mShutdownIntentReceiver = - new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - Log.w(TAG, "Writing sync state before shutdown..."); - getSyncStorageEngine().writeAllState(); - } - }; - - private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); - if (userId == UserHandle.USER_NULL) return; - - if (Intent.ACTION_USER_REMOVED.equals(action)) { - onUserRemoved(userId); - } else if (Intent.ACTION_USER_STARTING.equals(action)) { - onUserStarting(userId); - } else if (Intent.ACTION_USER_STOPPING.equals(action)) { - onUserStopping(userId); - } - } - }; - - private static final String ACTION_SYNC_ALARM = "android.content.syncmanager.SYNC_ALARM"; - private final SyncHandler mSyncHandler; - - private volatile boolean mBootCompleted = false; - - private ConnectivityManager getConnectivityManager() { - synchronized (this) { - if (mConnManagerDoNotUseDirectly == null) { - mConnManagerDoNotUseDirectly = (ConnectivityManager)mContext.getSystemService( - Context.CONNECTIVITY_SERVICE); - } - return mConnManagerDoNotUseDirectly; - } - } - - /** - * Should only be created after {@link ContentService#systemReady()} so that - * {@link PackageManager} is ready to query. - */ - public SyncManager(Context context, boolean factoryTest) { - // Initialize the SyncStorageEngine first, before registering observers - // and creating threads and so on; it may fail if the disk is full. - mContext = context; - - SyncStorageEngine.init(context); - mSyncStorageEngine = SyncStorageEngine.getSingleton(); - mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() { - public void onSyncRequest(Account account, int userId, int reason, String authority, - Bundle extras) { - scheduleSync(account, userId, reason, authority, extras, 0, false); - } - }); - - mSyncAdapters = new SyncAdaptersCache(mContext); - mSyncQueue = new SyncQueue(mContext.getPackageManager(), mSyncStorageEngine, mSyncAdapters); - - HandlerThread syncThread = new HandlerThread("SyncHandlerThread", - Process.THREAD_PRIORITY_BACKGROUND); - syncThread.start(); - mSyncHandler = new SyncHandler(syncThread.getLooper()); - - mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() { - @Override - public void onServiceChanged(SyncAdapterType type, int userId, boolean removed) { - if (!removed) { - scheduleSync(null, UserHandle.USER_ALL, - SyncOperation.REASON_SERVICE_CHANGED, - type.authority, null, 0 /* no delay */, - false /* onlyThoseWithUnkownSyncableState */); - } - } - }, mSyncHandler); - - mSyncAlarmIntent = PendingIntent.getBroadcast( - mContext, 0 /* ignored */, new Intent(ACTION_SYNC_ALARM), 0); - - IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); - context.registerReceiver(mConnectivityIntentReceiver, intentFilter); - - if (!factoryTest) { - intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); - context.registerReceiver(mBootCompletedReceiver, intentFilter); - } - - intentFilter = new IntentFilter(ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED); - context.registerReceiver(mBackgroundDataSettingChanged, intentFilter); - - intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW); - intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); - context.registerReceiver(mStorageIntentReceiver, intentFilter); - - intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN); - intentFilter.setPriority(100); - context.registerReceiver(mShutdownIntentReceiver, intentFilter); - - intentFilter = new IntentFilter(); - intentFilter.addAction(Intent.ACTION_USER_REMOVED); - intentFilter.addAction(Intent.ACTION_USER_STARTING); - intentFilter.addAction(Intent.ACTION_USER_STOPPING); - mContext.registerReceiverAsUser( - mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null); - - if (!factoryTest) { - mNotificationMgr = (NotificationManager) - context.getSystemService(Context.NOTIFICATION_SERVICE); - context.registerReceiver(new SyncAlarmIntentReceiver(), - new IntentFilter(ACTION_SYNC_ALARM)); - } else { - mNotificationMgr = null; - } - mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - - // This WakeLock is used to ensure that we stay awake between the time that we receive - // a sync alarm notification and when we finish processing it. We need to do this - // because we don't do the work in the alarm handler, rather we do it in a message - // handler. - mHandleAlarmWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, - HANDLE_SYNC_ALARM_WAKE_LOCK); - mHandleAlarmWakeLock.setReferenceCounted(false); - - // This WakeLock is used to ensure that we stay awake while running the sync loop - // message handler. Normally we will hold a sync adapter wake lock while it is being - // synced but during the execution of the sync loop it might finish a sync for - // one sync adapter before starting the sync for the other sync adapter and we - // don't want the device to go to sleep during that window. - mSyncManagerWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, - SYNC_LOOP_WAKE_LOCK); - mSyncManagerWakeLock.setReferenceCounted(false); - - mSyncStorageEngine.addStatusChangeListener( - ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, new ISyncStatusObserver.Stub() { - public void onStatusChanged(int which) { - // force the sync loop to run if the settings change - sendCheckAlarmsMessage(); - } - }); - - if (!factoryTest) { - // Register for account list updates for all users - mContext.registerReceiverAsUser(mAccountsUpdatedReceiver, - UserHandle.ALL, - new IntentFilter(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION), - null, null); - } - - // Pick a random second in a day to seed all periodic syncs - mSyncRandomOffsetMillis = mSyncStorageEngine.getSyncRandomOffset() * 1000; - } - - /** - * Return a random value v that satisfies minValue <= v < maxValue. The difference between - * maxValue and minValue must be less than Integer.MAX_VALUE. - */ - private long jitterize(long minValue, long maxValue) { - Random random = new Random(SystemClock.elapsedRealtime()); - long spread = maxValue - minValue; - if (spread > Integer.MAX_VALUE) { - throw new IllegalArgumentException("the difference between the maxValue and the " - + "minValue must be less than " + Integer.MAX_VALUE); - } - return minValue + random.nextInt((int)spread); - } - - public SyncStorageEngine getSyncStorageEngine() { - return mSyncStorageEngine; - } - - private void ensureAlarmService() { - if (mAlarmService == null) { - mAlarmService = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); - } - } - - /** - * Initiate a sync. This can start a sync for all providers - * (pass null to url, set onlyTicklable to false), only those - * providers that are marked as ticklable (pass null to url, - * set onlyTicklable to true), or a specific provider (set url - * to the content url of the provider). - * - * <p>If the ContentResolver.SYNC_EXTRAS_UPLOAD boolean in extras is - * true then initiate a sync that just checks for local changes to send - * to the server, otherwise initiate a sync that first gets any - * changes from the server before sending local changes back to - * the server. - * - * <p>If a specific provider is being synced (the url is non-null) - * then the extras can contain SyncAdapter-specific information - * to control what gets synced (e.g. which specific feed to sync). - * - * <p>You'll start getting callbacks after this. - * - * @param requestedAccount the account to sync, may be null to signify all accounts - * @param userId the id of the user whose accounts are to be synced. If userId is USER_ALL, - * then all users' accounts are considered. - * @param reason for sync request. If this is a positive integer, it is the Linux uid - * assigned to the process that requested the sync. If it's negative, the sync was requested by - * the SyncManager itself and could be one of the following: - * {@link SyncOperation#REASON_BACKGROUND_DATA_SETTINGS_CHANGED} - * {@link SyncOperation#REASON_ACCOUNTS_UPDATED} - * {@link SyncOperation#REASON_SERVICE_CHANGED} - * {@link SyncOperation#REASON_PERIODIC} - * {@link SyncOperation#REASON_IS_SYNCABLE} - * {@link SyncOperation#REASON_SYNC_AUTO} - * {@link SyncOperation#REASON_MASTER_SYNC_AUTO} - * {@link SyncOperation#REASON_USER_START} - * @param requestedAuthority the authority to sync, may be null to indicate all authorities - * @param extras a Map of SyncAdapter-specific information to control - * syncs of a specific provider. Can be null. Is ignored - * if the url is null. - * @param delay how many milliseconds in the future to wait before performing this - * @param onlyThoseWithUnkownSyncableState - */ - public void scheduleSync(Account requestedAccount, int userId, int reason, - String requestedAuthority, Bundle extras, long delay, - boolean onlyThoseWithUnkownSyncableState) { - boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); - - final boolean backgroundDataUsageAllowed = !mBootCompleted || - getConnectivityManager().getBackgroundDataSetting(); - - if (extras == null) extras = new Bundle(); - - Boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false); - if (expedited) { - delay = -1; // this means schedule at the front of the queue - } - - AccountAndUser[] accounts; - if (requestedAccount != null && userId != UserHandle.USER_ALL) { - accounts = new AccountAndUser[] { new AccountAndUser(requestedAccount, userId) }; - } else { - // if the accounts aren't configured yet then we can't support an account-less - // sync request - accounts = mRunningAccounts; - if (accounts.length == 0) { - if (isLoggable) { - Log.v(TAG, "scheduleSync: no accounts configured, dropping"); - } - return; - } - } - - final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false); - final boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false); - if (manualSync) { - extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true); - extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); - } - final boolean ignoreSettings = - extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false); - - int source; - if (uploadOnly) { - source = SyncStorageEngine.SOURCE_LOCAL; - } else if (manualSync) { - source = SyncStorageEngine.SOURCE_USER; - } else if (requestedAuthority == null) { - source = SyncStorageEngine.SOURCE_POLL; - } else { - // this isn't strictly server, since arbitrary callers can (and do) request - // a non-forced two-way sync on a specific url - source = SyncStorageEngine.SOURCE_SERVER; - } - - for (AccountAndUser account : accounts) { - // Compile a list of authorities that have sync adapters. - // For each authority sync each account that matches a sync adapter. - final HashSet<String> syncableAuthorities = new HashSet<String>(); - for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter : - mSyncAdapters.getAllServices(account.userId)) { - syncableAuthorities.add(syncAdapter.type.authority); - } - - // if the url was specified then replace the list of authorities - // with just this authority or clear it if this authority isn't - // syncable - if (requestedAuthority != null) { - final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority); - syncableAuthorities.clear(); - if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority); - } - - for (String authority : syncableAuthorities) { - int isSyncable = mSyncStorageEngine.getIsSyncable(account.account, account.userId, - authority); - if (isSyncable == 0) { - continue; - } - final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; - syncAdapterInfo = mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey(authority, account.account.type), account.userId); - if (syncAdapterInfo == null) { - continue; - } - final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs(); - final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable(); - if (isSyncable < 0 && isAlwaysSyncable) { - mSyncStorageEngine.setIsSyncable(account.account, account.userId, authority, 1); - isSyncable = 1; - } - if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) { - continue; - } - if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) { - continue; - } - - // always allow if the isSyncable state is unknown - boolean syncAllowed = - (isSyncable < 0) - || ignoreSettings - || (backgroundDataUsageAllowed - && mSyncStorageEngine.getMasterSyncAutomatically(account.userId) - && mSyncStorageEngine.getSyncAutomatically(account.account, - account.userId, authority)); - if (!syncAllowed) { - if (isLoggable) { - Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority - + " is not allowed, dropping request"); - } - continue; - } - - Pair<Long, Long> backoff = mSyncStorageEngine - .getBackoff(account.account, account.userId, authority); - long delayUntil = mSyncStorageEngine.getDelayUntilTime(account.account, - account.userId, authority); - final long backoffTime = backoff != null ? backoff.first : 0; - if (isSyncable < 0) { - Bundle newExtras = new Bundle(); - newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true); - if (isLoggable) { - Log.v(TAG, "scheduleSync:" - + " delay " + delay - + ", source " + source - + ", account " + account - + ", authority " + authority - + ", extras " + newExtras); - } - scheduleSyncOperation( - new SyncOperation(account.account, account.userId, reason, source, - authority, newExtras, 0, backoffTime, delayUntil, - allowParallelSyncs)); - } - if (!onlyThoseWithUnkownSyncableState) { - if (isLoggable) { - Log.v(TAG, "scheduleSync:" - + " delay " + delay - + ", source " + source - + ", account " + account - + ", authority " + authority - + ", extras " + extras); - } - scheduleSyncOperation( - new SyncOperation(account.account, account.userId, reason, source, - authority, extras, delay, backoffTime, delayUntil, - allowParallelSyncs)); - } - } - } - } - - public void scheduleLocalSync(Account account, int userId, int reason, String authority) { - final Bundle extras = new Bundle(); - extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true); - scheduleSync(account, userId, reason, authority, extras, LOCAL_SYNC_DELAY, - false /* onlyThoseWithUnkownSyncableState */); - } - - public SyncAdapterType[] getSyncAdapterTypes(int userId) { - final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos; - serviceInfos = mSyncAdapters.getAllServices(userId); - SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()]; - int i = 0; - for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) { - types[i] = serviceInfo.type; - ++i; - } - return types; - } - - private void sendSyncAlarmMessage() { - if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_SYNC_ALARM"); - mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_SYNC_ALARM); - } - - private void sendCheckAlarmsMessage() { - if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CHECK_ALARMS"); - mSyncHandler.removeMessages(SyncHandler.MESSAGE_CHECK_ALARMS); - mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_CHECK_ALARMS); - } - - private void sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext, - SyncResult syncResult) { - if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_SYNC_FINISHED"); - Message msg = mSyncHandler.obtainMessage(); - msg.what = SyncHandler.MESSAGE_SYNC_FINISHED; - msg.obj = new SyncHandlerMessagePayload(syncContext, syncResult); - mSyncHandler.sendMessage(msg); - } - - private void sendCancelSyncsMessage(final Account account, final int userId, - final String authority) { - if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CANCEL"); - Message msg = mSyncHandler.obtainMessage(); - msg.what = SyncHandler.MESSAGE_CANCEL; - msg.obj = Pair.create(account, authority); - msg.arg1 = userId; - mSyncHandler.sendMessage(msg); - } - - class SyncHandlerMessagePayload { - public final ActiveSyncContext activeSyncContext; - public final SyncResult syncResult; - - SyncHandlerMessagePayload(ActiveSyncContext syncContext, SyncResult syncResult) { - this.activeSyncContext = syncContext; - this.syncResult = syncResult; - } - } - - class SyncAlarmIntentReceiver extends BroadcastReceiver { - public void onReceive(Context context, Intent intent) { - mHandleAlarmWakeLock.acquire(); - sendSyncAlarmMessage(); - } - } - - private void clearBackoffSetting(SyncOperation op) { - mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority, - SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE); - synchronized (mSyncQueue) { - mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, 0); - } - } - - private void increaseBackoffSetting(SyncOperation op) { - // TODO: Use this function to align it to an already scheduled sync - // operation in the specified window - final long now = SystemClock.elapsedRealtime(); - - final Pair<Long, Long> previousSettings = - mSyncStorageEngine.getBackoff(op.account, op.userId, op.authority); - long newDelayInMs = -1; - if (previousSettings != null) { - // don't increase backoff before current backoff is expired. This will happen for op's - // with ignoreBackoff set. - if (now < previousSettings.first) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Still in backoff, do not increase it. " - + "Remaining: " + ((previousSettings.first - now) / 1000) + " seconds."); - } - return; - } - // Subsequent delays are the double of the previous delay - newDelayInMs = previousSettings.second * 2; - } - if (newDelayInMs <= 0) { - // The initial delay is the jitterized INITIAL_SYNC_RETRY_TIME_IN_MS - newDelayInMs = jitterize(INITIAL_SYNC_RETRY_TIME_IN_MS, - (long)(INITIAL_SYNC_RETRY_TIME_IN_MS * 1.1)); - } - - // Cap the delay - long maxSyncRetryTimeInSeconds = Settings.Global.getLong(mContext.getContentResolver(), - Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS, - DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS); - if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) { - newDelayInMs = maxSyncRetryTimeInSeconds * 1000; - } - - final long backoff = now + newDelayInMs; - - mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority, - backoff, newDelayInMs); - - op.backoff = backoff; - op.updateEffectiveRunTime(); - - synchronized (mSyncQueue) { - mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, backoff); - } - } - - private void setDelayUntilTime(SyncOperation op, long delayUntilSeconds) { - final long delayUntil = delayUntilSeconds * 1000; - final long absoluteNow = System.currentTimeMillis(); - long newDelayUntilTime; - if (delayUntil > absoluteNow) { - newDelayUntilTime = SystemClock.elapsedRealtime() + (delayUntil - absoluteNow); - } else { - newDelayUntilTime = 0; - } - mSyncStorageEngine - .setDelayUntilTime(op.account, op.userId, op.authority, newDelayUntilTime); - synchronized (mSyncQueue) { - mSyncQueue.onDelayUntilTimeChanged(op.account, op.authority, newDelayUntilTime); - } - } - - /** - * Cancel the active sync if it matches the authority and account. - * @param account limit the cancelations to syncs with this account, if non-null - * @param authority limit the cancelations to syncs with this authority, if non-null - */ - public void cancelActiveSync(Account account, int userId, String authority) { - sendCancelSyncsMessage(account, userId, authority); - } - - /** - * Create and schedule a SyncOperation. - * - * @param syncOperation the SyncOperation to schedule - */ - public void scheduleSyncOperation(SyncOperation syncOperation) { - boolean queueChanged; - synchronized (mSyncQueue) { - queueChanged = mSyncQueue.add(syncOperation); - } - - if (queueChanged) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "scheduleSyncOperation: enqueued " + syncOperation); - } - sendCheckAlarmsMessage(); - } else { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "scheduleSyncOperation: dropping duplicate sync operation " - + syncOperation); - } - } - } - - /** - * Remove scheduled sync operations. - * @param account limit the removals to operations with this account, if non-null - * @param authority limit the removals to operations with this authority, if non-null - */ - public void clearScheduledSyncOperations(Account account, int userId, String authority) { - synchronized (mSyncQueue) { - mSyncQueue.remove(account, userId, authority); - } - mSyncStorageEngine.setBackoff(account, userId, authority, - SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE); - } - - void maybeRescheduleSync(SyncResult syncResult, SyncOperation operation) { - boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG); - if (isLoggable) { - Log.d(TAG, "encountered error(s) during the sync: " + syncResult + ", " + operation); - } - - operation = new SyncOperation(operation); - - // The SYNC_EXTRAS_IGNORE_BACKOFF only applies to the first attempt to sync a given - // request. Retries of the request will always honor the backoff, so clear the - // flag in case we retry this request. - if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)) { - operation.extras.remove(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF); - } - - // If this sync aborted because the internal sync loop retried too many times then - // don't reschedule. Otherwise we risk getting into a retry loop. - // If the operation succeeded to some extent then retry immediately. - // If this was a two-way sync then retry soft errors with an exponential backoff. - // If this was an upward sync then schedule a two-way sync immediately. - // Otherwise do not reschedule. - if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)) { - Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified " - + operation); - } else if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false) - && !syncResult.syncAlreadyInProgress) { - operation.extras.remove(ContentResolver.SYNC_EXTRAS_UPLOAD); - Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync " - + "encountered an error: " + operation); - scheduleSyncOperation(operation); - } else if (syncResult.tooManyRetries) { - Log.d(TAG, "not retrying sync operation because it retried too many times: " - + operation); - } else if (syncResult.madeSomeProgress()) { - if (isLoggable) { - Log.d(TAG, "retrying sync operation because even though it had an error " - + "it achieved some success"); - } - scheduleSyncOperation(operation); - } else if (syncResult.syncAlreadyInProgress) { - if (isLoggable) { - Log.d(TAG, "retrying sync operation that failed because there was already a " - + "sync in progress: " + operation); - } - scheduleSyncOperation(new SyncOperation(operation.account, operation.userId, - operation.reason, - operation.syncSource, - operation.authority, operation.extras, - DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000, - operation.backoff, operation.delayUntil, operation.allowParallelSyncs)); - } else if (syncResult.hasSoftError()) { - if (isLoggable) { - Log.d(TAG, "retrying sync operation because it encountered a soft error: " - + operation); - } - scheduleSyncOperation(operation); - } else { - Log.d(TAG, "not retrying sync operation because the error is a hard error: " - + operation); - } - } - - private void onUserStarting(int userId) { - // Make sure that accounts we're about to use are valid - AccountManagerService.getSingleton().validateAccounts(userId); - - mSyncAdapters.invalidateCache(userId); - - updateRunningAccounts(); - - synchronized (mSyncQueue) { - mSyncQueue.addPendingOperations(userId); - } - - // Schedule sync for any accounts under started user - final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId); - for (Account account : accounts) { - scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null, - 0 /* no delay */, true /* onlyThoseWithUnknownSyncableState */); - } - - sendCheckAlarmsMessage(); - } - - private void onUserStopping(int userId) { - updateRunningAccounts(); - - cancelActiveSync( - null /* any account */, - userId, - null /* any authority */); - } - - private void onUserRemoved(int userId) { - updateRunningAccounts(); - - // Clean up the storage engine database - mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId); - synchronized (mSyncQueue) { - mSyncQueue.removeUser(userId); - } - } - - /** - * @hide - */ - class ActiveSyncContext extends ISyncContext.Stub - implements ServiceConnection, IBinder.DeathRecipient { - final SyncOperation mSyncOperation; - final long mHistoryRowId; - ISyncAdapter mSyncAdapter; - final long mStartTime; - long mTimeoutStartTime; - boolean mBound; - final PowerManager.WakeLock mSyncWakeLock; - final int mSyncAdapterUid; - SyncInfo mSyncInfo; - boolean mIsLinkedToDeath = false; - - /** - * Create an ActiveSyncContext for an impending sync and grab the wakelock for that - * sync adapter. Since this grabs the wakelock you need to be sure to call - * close() when you are done with this ActiveSyncContext, whether the sync succeeded - * or not. - * @param syncOperation the SyncOperation we are about to sync - * @param historyRowId the row in which to record the history info for this sync - * @param syncAdapterUid the UID of the application that contains the sync adapter - * for this sync. This is used to attribute the wakelock hold to that application. - */ - public ActiveSyncContext(SyncOperation syncOperation, long historyRowId, - int syncAdapterUid) { - super(); - mSyncAdapterUid = syncAdapterUid; - mSyncOperation = syncOperation; - mHistoryRowId = historyRowId; - mSyncAdapter = null; - mStartTime = SystemClock.elapsedRealtime(); - mTimeoutStartTime = mStartTime; - mSyncWakeLock = mSyncHandler.getSyncWakeLock( - mSyncOperation.account, mSyncOperation.authority); - mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterUid)); - mSyncWakeLock.acquire(); - } - - public void sendHeartbeat() { - // heartbeats are no longer used - } - - public void onFinished(SyncResult result) { - if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "onFinished: " + this); - // include "this" in the message so that the handler can ignore it if this - // ActiveSyncContext is no longer the mActiveSyncContext at message handling - // time - sendSyncFinishedOrCanceledMessage(this, result); - } - - public void toString(StringBuilder sb) { - sb.append("startTime ").append(mStartTime) - .append(", mTimeoutStartTime ").append(mTimeoutStartTime) - .append(", mHistoryRowId ").append(mHistoryRowId) - .append(", syncOperation ").append(mSyncOperation); - } - - public void onServiceConnected(ComponentName name, IBinder service) { - Message msg = mSyncHandler.obtainMessage(); - msg.what = SyncHandler.MESSAGE_SERVICE_CONNECTED; - msg.obj = new ServiceConnectionData(this, ISyncAdapter.Stub.asInterface(service)); - mSyncHandler.sendMessage(msg); - } - - public void onServiceDisconnected(ComponentName name) { - Message msg = mSyncHandler.obtainMessage(); - msg.what = SyncHandler.MESSAGE_SERVICE_DISCONNECTED; - msg.obj = new ServiceConnectionData(this, null); - mSyncHandler.sendMessage(msg); - } - - boolean bindToSyncAdapter(RegisteredServicesCache.ServiceInfo info, int userId) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "bindToSyncAdapter: " + info.componentName + ", connection " + this); - } - Intent intent = new Intent(); - intent.setAction("android.content.SyncAdapter"); - intent.setComponent(info.componentName); - intent.putExtra(Intent.EXTRA_CLIENT_LABEL, - com.android.internal.R.string.sync_binding_label); - intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser( - mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0, - null, new UserHandle(userId))); - mBound = true; - final boolean bindResult = mContext.bindService(intent, this, - Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND - | Context.BIND_ALLOW_OOM_MANAGEMENT, - mSyncOperation.userId); - if (!bindResult) { - mBound = false; - } - return bindResult; - } - - /** - * Performs the required cleanup, which is the releasing of the wakelock and - * unbinding from the sync adapter (if actually bound). - */ - protected void close() { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "unBindFromSyncAdapter: connection " + this); - } - if (mBound) { - mBound = false; - mContext.unbindService(this); - } - mSyncWakeLock.release(); - mSyncWakeLock.setWorkSource(null); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - toString(sb); - return sb.toString(); - } - - @Override - public void binderDied() { - sendSyncFinishedOrCanceledMessage(this, null); - } - } - - protected void dump(FileDescriptor fd, PrintWriter pw) { - final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); - dumpSyncState(ipw); - dumpSyncHistory(ipw); - dumpSyncAdapters(ipw); - } - - static String formatTime(long time) { - Time tobj = new Time(); - tobj.set(time); - return tobj.format("%Y-%m-%d %H:%M:%S"); - } - - protected void dumpSyncState(PrintWriter pw) { - pw.print("data connected: "); pw.println(mDataConnectionIsConnected); - pw.print("auto sync: "); - List<UserInfo> users = getAllUsers(); - if (users != null) { - for (UserInfo user : users) { - pw.print("u" + user.id + "=" - + mSyncStorageEngine.getMasterSyncAutomatically(user.id) + " "); - } - pw.println(); - } - pw.print("memory low: "); pw.println(mStorageIsLow); - - final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts(); - - pw.print("accounts: "); - if (accounts != INITIAL_ACCOUNTS_ARRAY) { - pw.println(accounts.length); - } else { - pw.println("not known yet"); - } - final long now = SystemClock.elapsedRealtime(); - pw.print("now: "); pw.print(now); - pw.println(" (" + formatTime(System.currentTimeMillis()) + ")"); - pw.print("offset: "); pw.print(DateUtils.formatElapsedTime(mSyncRandomOffsetMillis/1000)); - pw.println(" (HH:MM:SS)"); - pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now/1000)); - pw.println(" (HH:MM:SS)"); - pw.print("time spent syncing: "); - pw.print(DateUtils.formatElapsedTime( - mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000)); - pw.print(" (HH:MM:SS), sync "); - pw.print(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not "); - pw.println("in progress"); - if (mSyncHandler.mAlarmScheduleTime != null) { - pw.print("next alarm time: "); pw.print(mSyncHandler.mAlarmScheduleTime); - pw.print(" ("); - pw.print(DateUtils.formatElapsedTime((mSyncHandler.mAlarmScheduleTime-now)/1000)); - pw.println(" (HH:MM:SS) from now)"); - } else { - pw.println("no alarm is scheduled (there had better not be any pending syncs)"); - } - - pw.print("notification info: "); - final StringBuilder sb = new StringBuilder(); - mSyncHandler.mSyncNotificationInfo.toString(sb); - pw.println(sb.toString()); - - pw.println(); - pw.println("Active Syncs: " + mActiveSyncContexts.size()); - final PackageManager pm = mContext.getPackageManager(); - for (SyncManager.ActiveSyncContext activeSyncContext : mActiveSyncContexts) { - final long durationInSeconds = (now - activeSyncContext.mStartTime) / 1000; - pw.print(" "); - pw.print(DateUtils.formatElapsedTime(durationInSeconds)); - pw.print(" - "); - pw.print(activeSyncContext.mSyncOperation.dump(pm, false)); - pw.println(); - } - - synchronized (mSyncQueue) { - sb.setLength(0); - mSyncQueue.dump(sb); - } - pw.println(); - pw.print(sb.toString()); - - // join the installed sync adapter with the accounts list and emit for everything - pw.println(); - pw.println("Sync Status"); - for (AccountAndUser account : accounts) { - pw.printf("Account %s u%d %s\n", - account.account.name, account.userId, account.account.type); - - pw.println("======================================================================="); - final PrintTable table = new PrintTable(13); - table.set(0, 0, - "Authority", // 0 - "Syncable", // 1 - "Enabled", // 2 - "Delay", // 3 - "Loc", // 4 - "Poll", // 5 - "Per", // 6 - "Serv", // 7 - "User", // 8 - "Tot", // 9 - "Time", // 10 - "Last Sync", // 11 - "Periodic" // 12 - ); - - final List<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> sorted = - Lists.newArrayList(); - sorted.addAll(mSyncAdapters.getAllServices(account.userId)); - Collections.sort(sorted, - new Comparator<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>() { - @Override - public int compare(RegisteredServicesCache.ServiceInfo<SyncAdapterType> lhs, - RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) { - return lhs.type.authority.compareTo(rhs.type.authority); - } - }); - for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType : sorted) { - if (!syncAdapterType.type.accountType.equals(account.account.type)) { - continue; - } - int row = table.getNumRows(); - SyncStorageEngine.AuthorityInfo settings = - mSyncStorageEngine.getOrCreateAuthority( - account.account, account.userId, syncAdapterType.type.authority); - SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(settings); - - String authority = settings.authority; - if (authority.length() > 50) { - authority = authority.substring(authority.length() - 50); - } - table.set(row, 0, authority, settings.syncable, settings.enabled); - table.set(row, 4, - status.numSourceLocal, - status.numSourcePoll, - status.numSourcePeriodic, - status.numSourceServer, - status.numSourceUser, - status.numSyncs, - DateUtils.formatElapsedTime(status.totalElapsedTime / 1000)); - - - for (int i = 0; i < settings.periodicSyncs.size(); i++) { - final Pair<Bundle, Long> pair = settings.periodicSyncs.get(0); - final String period = String.valueOf(pair.second); - final String extras = pair.first.size() > 0 ? pair.first.toString() : ""; - final String next = formatTime(status.getPeriodicSyncTime(0) - + pair.second * 1000); - table.set(row + i * 2, 12, period + extras); - table.set(row + i * 2 + 1, 12, next); - } - - int row1 = row; - if (settings.delayUntil > now) { - table.set(row1++, 12, "D: " + (settings.delayUntil - now) / 1000); - if (settings.backoffTime > now) { - table.set(row1++, 12, "B: " + (settings.backoffTime - now) / 1000); - table.set(row1++, 12, settings.backoffDelay / 1000); - } - } - - if (status.lastSuccessTime != 0) { - table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastSuccessSource] - + " " + "SUCCESS"); - table.set(row1++, 11, formatTime(status.lastSuccessTime)); - } - if (status.lastFailureTime != 0) { - table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastFailureSource] - + " " + "FAILURE"); - table.set(row1++, 11, formatTime(status.lastFailureTime)); - //noinspection UnusedAssignment - table.set(row1++, 11, status.lastFailureMesg); - } - } - table.writeTo(pw); - } - } - - private String getLastFailureMessage(int code) { - switch (code) { - case ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS: - return "sync already in progress"; - - case ContentResolver.SYNC_ERROR_AUTHENTICATION: - return "authentication error"; - - case ContentResolver.SYNC_ERROR_IO: - return "I/O error"; - - case ContentResolver.SYNC_ERROR_PARSE: - return "parse error"; - - case ContentResolver.SYNC_ERROR_CONFLICT: - return "conflict error"; - - case ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS: - return "too many deletions error"; - - case ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES: - return "too many retries error"; - - case ContentResolver.SYNC_ERROR_INTERNAL: - return "internal error"; - - default: - return "unknown"; - } - } - - private void dumpTimeSec(PrintWriter pw, long time) { - pw.print(time/1000); pw.print('.'); pw.print((time/100)%10); - pw.print('s'); - } - - private void dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds) { - pw.print("Success ("); pw.print(ds.successCount); - if (ds.successCount > 0) { - pw.print(" for "); dumpTimeSec(pw, ds.successTime); - pw.print(" avg="); dumpTimeSec(pw, ds.successTime/ds.successCount); - } - pw.print(") Failure ("); pw.print(ds.failureCount); - if (ds.failureCount > 0) { - pw.print(" for "); dumpTimeSec(pw, ds.failureTime); - pw.print(" avg="); dumpTimeSec(pw, ds.failureTime/ds.failureCount); - } - pw.println(")"); - } - - protected void dumpSyncHistory(PrintWriter pw) { - dumpRecentHistory(pw); - dumpDayStatistics(pw); - } - - private void dumpRecentHistory(PrintWriter pw) { - final ArrayList<SyncStorageEngine.SyncHistoryItem> items - = mSyncStorageEngine.getSyncHistory(); - if (items != null && items.size() > 0) { - final Map<String, AuthoritySyncStats> authorityMap = Maps.newHashMap(); - long totalElapsedTime = 0; - long totalTimes = 0; - final int N = items.size(); - - int maxAuthority = 0; - int maxAccount = 0; - for (SyncStorageEngine.SyncHistoryItem item : items) { - SyncStorageEngine.AuthorityInfo authority - = mSyncStorageEngine.getAuthority(item.authorityId); - final String authorityName; - final String accountKey; - if (authority != null) { - authorityName = authority.authority; - accountKey = authority.account.name + "/" + authority.account.type - + " u" + authority.userId; - } else { - authorityName = "Unknown"; - accountKey = "Unknown"; - } - - int length = authorityName.length(); - if (length > maxAuthority) { - maxAuthority = length; - } - length = accountKey.length(); - if (length > maxAccount) { - maxAccount = length; - } - - final long elapsedTime = item.elapsedTime; - totalElapsedTime += elapsedTime; - totalTimes++; - AuthoritySyncStats authoritySyncStats = authorityMap.get(authorityName); - if (authoritySyncStats == null) { - authoritySyncStats = new AuthoritySyncStats(authorityName); - authorityMap.put(authorityName, authoritySyncStats); - } - authoritySyncStats.elapsedTime += elapsedTime; - authoritySyncStats.times++; - final Map<String, AccountSyncStats> accountMap = authoritySyncStats.accountMap; - AccountSyncStats accountSyncStats = accountMap.get(accountKey); - if (accountSyncStats == null) { - accountSyncStats = new AccountSyncStats(accountKey); - accountMap.put(accountKey, accountSyncStats); - } - accountSyncStats.elapsedTime += elapsedTime; - accountSyncStats.times++; - - } - - if (totalElapsedTime > 0) { - pw.println(); - pw.printf("Detailed Statistics (Recent history): " - + "%d (# of times) %ds (sync time)\n", - totalTimes, totalElapsedTime / 1000); - - final List<AuthoritySyncStats> sortedAuthorities = - new ArrayList<AuthoritySyncStats>(authorityMap.values()); - Collections.sort(sortedAuthorities, new Comparator<AuthoritySyncStats>() { - @Override - public int compare(AuthoritySyncStats lhs, AuthoritySyncStats rhs) { - // reverse order - int compare = Integer.compare(rhs.times, lhs.times); - if (compare == 0) { - compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime); - } - return compare; - } - }); - - final int maxLength = Math.max(maxAuthority, maxAccount + 3); - final int padLength = 2 + 2 + maxLength + 2 + 10 + 11; - final char chars[] = new char[padLength]; - Arrays.fill(chars, '-'); - final String separator = new String(chars); - - final String authorityFormat = - String.format(" %%-%ds: %%-9s %%-11s\n", maxLength + 2); - final String accountFormat = - String.format(" %%-%ds: %%-9s %%-11s\n", maxLength); - - pw.println(separator); - for (AuthoritySyncStats authoritySyncStats : sortedAuthorities) { - String name = authoritySyncStats.name; - long elapsedTime; - int times; - String timeStr; - String timesStr; - - elapsedTime = authoritySyncStats.elapsedTime; - times = authoritySyncStats.times; - timeStr = String.format("%ds/%d%%", - elapsedTime / 1000, - elapsedTime * 100 / totalElapsedTime); - timesStr = String.format("%d/%d%%", - times, - times * 100 / totalTimes); - pw.printf(authorityFormat, name, timesStr, timeStr); - - final List<AccountSyncStats> sortedAccounts = - new ArrayList<AccountSyncStats>( - authoritySyncStats.accountMap.values()); - Collections.sort(sortedAccounts, new Comparator<AccountSyncStats>() { - @Override - public int compare(AccountSyncStats lhs, AccountSyncStats rhs) { - // reverse order - int compare = Integer.compare(rhs.times, lhs.times); - if (compare == 0) { - compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime); - } - return compare; - } - }); - for (AccountSyncStats stats: sortedAccounts) { - elapsedTime = stats.elapsedTime; - times = stats.times; - timeStr = String.format("%ds/%d%%", - elapsedTime / 1000, - elapsedTime * 100 / totalElapsedTime); - timesStr = String.format("%d/%d%%", - times, - times * 100 / totalTimes); - pw.printf(accountFormat, stats.name, timesStr, timeStr); - } - pw.println(separator); - } - } - - pw.println(); - pw.println("Recent Sync History"); - final String format = " %-" + maxAccount + "s %-" + maxAuthority + "s %s\n"; - final Map<String, Long> lastTimeMap = Maps.newHashMap(); - final PackageManager pm = mContext.getPackageManager(); - for (int i = 0; i < N; i++) { - SyncStorageEngine.SyncHistoryItem item = items.get(i); - SyncStorageEngine.AuthorityInfo authority - = mSyncStorageEngine.getAuthority(item.authorityId); - final String authorityName; - final String accountKey; - if (authority != null) { - authorityName = authority.authority; - accountKey = authority.account.name + "/" + authority.account.type - + " u" + authority.userId; - } else { - authorityName = "Unknown"; - accountKey = "Unknown"; - } - final long elapsedTime = item.elapsedTime; - final Time time = new Time(); - final long eventTime = item.eventTime; - time.set(eventTime); - - final String key = authorityName + "/" + accountKey; - final Long lastEventTime = lastTimeMap.get(key); - final String diffString; - if (lastEventTime == null) { - diffString = ""; - } else { - final long diff = (lastEventTime - eventTime) / 1000; - if (diff < 60) { - diffString = String.valueOf(diff); - } else if (diff < 3600) { - diffString = String.format("%02d:%02d", diff / 60, diff % 60); - } else { - final long sec = diff % 3600; - diffString = String.format("%02d:%02d:%02d", - diff / 3600, sec / 60, sec % 60); - } - } - lastTimeMap.put(key, eventTime); - - pw.printf(" #%-3d: %s %8s %5.1fs %8s", - i + 1, - formatTime(eventTime), - SyncStorageEngine.SOURCES[item.source], - ((float) elapsedTime) / 1000, - diffString); - pw.printf(format, accountKey, authorityName, - SyncOperation.reasonToString(pm, item.reason)); - - if (item.event != SyncStorageEngine.EVENT_STOP - || item.upstreamActivity != 0 - || item.downstreamActivity != 0) { - pw.printf(" event=%d upstreamActivity=%d downstreamActivity=%d\n", - item.event, - item.upstreamActivity, - item.downstreamActivity); - } - if (item.mesg != null - && !SyncStorageEngine.MESG_SUCCESS.equals(item.mesg)) { - pw.printf(" mesg=%s\n", item.mesg); - } - } - pw.println(); - pw.println("Recent Sync History Extras"); - for (int i = 0; i < N; i++) { - final SyncStorageEngine.SyncHistoryItem item = items.get(i); - final Bundle extras = item.extras; - if (extras == null || extras.size() == 0) { - continue; - } - final SyncStorageEngine.AuthorityInfo authority - = mSyncStorageEngine.getAuthority(item.authorityId); - final String authorityName; - final String accountKey; - if (authority != null) { - authorityName = authority.authority; - accountKey = authority.account.name + "/" + authority.account.type - + " u" + authority.userId; - } else { - authorityName = "Unknown"; - accountKey = "Unknown"; - } - final Time time = new Time(); - final long eventTime = item.eventTime; - time.set(eventTime); - - pw.printf(" #%-3d: %s %8s ", - i + 1, - formatTime(eventTime), - SyncStorageEngine.SOURCES[item.source]); - - pw.printf(format, accountKey, authorityName, extras); - } - } - } - - private void dumpDayStatistics(PrintWriter pw) { - SyncStorageEngine.DayStats dses[] = mSyncStorageEngine.getDayStatistics(); - if (dses != null && dses[0] != null) { - pw.println(); - pw.println("Sync Statistics"); - pw.print(" Today: "); dumpDayStatistic(pw, dses[0]); - int today = dses[0].day; - int i; - SyncStorageEngine.DayStats ds; - - // Print each day in the current week. - for (i=1; i<=6 && i < dses.length; i++) { - ds = dses[i]; - if (ds == null) break; - int delta = today-ds.day; - if (delta > 6) break; - - pw.print(" Day-"); pw.print(delta); pw.print(": "); - dumpDayStatistic(pw, ds); - } - - // Aggregate all following days into weeks and print totals. - int weekDay = today; - while (i < dses.length) { - SyncStorageEngine.DayStats aggr = null; - weekDay -= 7; - while (i < dses.length) { - ds = dses[i]; - if (ds == null) { - i = dses.length; - break; - } - int delta = weekDay-ds.day; - if (delta > 6) break; - i++; - - if (aggr == null) { - aggr = new SyncStorageEngine.DayStats(weekDay); - } - aggr.successCount += ds.successCount; - aggr.successTime += ds.successTime; - aggr.failureCount += ds.failureCount; - aggr.failureTime += ds.failureTime; - } - if (aggr != null) { - pw.print(" Week-"); pw.print((today-weekDay)/7); pw.print(": "); - dumpDayStatistic(pw, aggr); - } - } - } - } - - private void dumpSyncAdapters(IndentingPrintWriter pw) { - pw.println(); - final List<UserInfo> users = getAllUsers(); - if (users != null) { - for (UserInfo user : users) { - pw.println("Sync adapters for " + user + ":"); - pw.increaseIndent(); - for (RegisteredServicesCache.ServiceInfo<?> info : - mSyncAdapters.getAllServices(user.id)) { - pw.println(info); - } - pw.decreaseIndent(); - pw.println(); - } - } - } - - private static class AuthoritySyncStats { - String name; - long elapsedTime; - int times; - Map<String, AccountSyncStats> accountMap = Maps.newHashMap(); - - private AuthoritySyncStats(String name) { - this.name = name; - } - } - - private static class AccountSyncStats { - String name; - long elapsedTime; - int times; - - private AccountSyncStats(String name) { - this.name = name; - } - } - - /** - * A helper object to keep track of the time we have spent syncing since the last boot - */ - private class SyncTimeTracker { - /** True if a sync was in progress on the most recent call to update() */ - boolean mLastWasSyncing = false; - /** Used to track when lastWasSyncing was last set */ - long mWhenSyncStarted = 0; - /** The cumulative time we have spent syncing */ - private long mTimeSpentSyncing; - - /** Call to let the tracker know that the sync state may have changed */ - public synchronized void update() { - final boolean isSyncInProgress = !mActiveSyncContexts.isEmpty(); - if (isSyncInProgress == mLastWasSyncing) return; - final long now = SystemClock.elapsedRealtime(); - if (isSyncInProgress) { - mWhenSyncStarted = now; - } else { - mTimeSpentSyncing += now - mWhenSyncStarted; - } - mLastWasSyncing = isSyncInProgress; - } - - /** Get how long we have been syncing, in ms */ - public synchronized long timeSpentSyncing() { - if (!mLastWasSyncing) return mTimeSpentSyncing; - - final long now = SystemClock.elapsedRealtime(); - return mTimeSpentSyncing + (now - mWhenSyncStarted); - } - } - - class ServiceConnectionData { - public final ActiveSyncContext activeSyncContext; - public final ISyncAdapter syncAdapter; - ServiceConnectionData(ActiveSyncContext activeSyncContext, ISyncAdapter syncAdapter) { - this.activeSyncContext = activeSyncContext; - this.syncAdapter = syncAdapter; - } - } - - /** - * Handles SyncOperation Messages that are posted to the associated - * HandlerThread. - */ - class SyncHandler extends Handler { - // Messages that can be sent on mHandler - private static final int MESSAGE_SYNC_FINISHED = 1; - private static final int MESSAGE_SYNC_ALARM = 2; - private static final int MESSAGE_CHECK_ALARMS = 3; - private static final int MESSAGE_SERVICE_CONNECTED = 4; - private static final int MESSAGE_SERVICE_DISCONNECTED = 5; - private static final int MESSAGE_CANCEL = 6; - - public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo(); - private Long mAlarmScheduleTime = null; - public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker(); - private final HashMap<Pair<Account, String>, PowerManager.WakeLock> mWakeLocks = - Maps.newHashMap(); - - private volatile CountDownLatch mReadyToRunLatch = new CountDownLatch(1); - - public void onBootCompleted() { - mBootCompleted = true; - - doDatabaseCleanup(); - - if (mReadyToRunLatch != null) { - mReadyToRunLatch.countDown(); - } - } - - private PowerManager.WakeLock getSyncWakeLock(Account account, String authority) { - final Pair<Account, String> wakeLockKey = Pair.create(account, authority); - PowerManager.WakeLock wakeLock = mWakeLocks.get(wakeLockKey); - if (wakeLock == null) { - final String name = SYNC_WAKE_LOCK_PREFIX + "_" + authority + "_" + account; - wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name); - wakeLock.setReferenceCounted(false); - mWakeLocks.put(wakeLockKey, wakeLock); - } - return wakeLock; - } - - private void waitUntilReadyToRun() { - CountDownLatch latch = mReadyToRunLatch; - if (latch != null) { - while (true) { - try { - latch.await(); - mReadyToRunLatch = null; - return; - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - } - } - /** - * Used to keep track of whether a sync notification is active and who it is for. - */ - class SyncNotificationInfo { - // true iff the notification manager has been asked to send the notification - public boolean isActive = false; - - // Set when we transition from not running a sync to running a sync, and cleared on - // the opposite transition. - public Long startTime = null; - - public void toString(StringBuilder sb) { - sb.append("isActive ").append(isActive).append(", startTime ").append(startTime); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - toString(sb); - return sb.toString(); - } - } - - public SyncHandler(Looper looper) { - super(looper); - } - - public void handleMessage(Message msg) { - long earliestFuturePollTime = Long.MAX_VALUE; - long nextPendingSyncTime = Long.MAX_VALUE; - - // Setting the value here instead of a method because we want the dumpsys logs - // to have the most recent value used. - try { - waitUntilReadyToRun(); - mDataConnectionIsConnected = readDataConnectionState(); - mSyncManagerWakeLock.acquire(); - // Always do this first so that we be sure that any periodic syncs that - // are ready to run have been converted into pending syncs. This allows the - // logic that considers the next steps to take based on the set of pending syncs - // to also take into account the periodic syncs. - earliestFuturePollTime = scheduleReadyPeriodicSyncs(); - switch (msg.what) { - case SyncHandler.MESSAGE_CANCEL: { - Pair<Account, String> payload = (Pair<Account, String>)msg.obj; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CANCEL: " - + payload.first + ", " + payload.second); - } - cancelActiveSyncLocked(payload.first, msg.arg1, payload.second); - nextPendingSyncTime = maybeStartNextSyncLocked(); - break; - } - - case SyncHandler.MESSAGE_SYNC_FINISHED: - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_FINISHED"); - } - SyncHandlerMessagePayload payload = (SyncHandlerMessagePayload)msg.obj; - if (!isSyncStillActive(payload.activeSyncContext)) { - Log.d(TAG, "handleSyncHandlerMessage: dropping since the " - + "sync is no longer active: " - + payload.activeSyncContext); - break; - } - runSyncFinishedOrCanceledLocked(payload.syncResult, payload.activeSyncContext); - - // since a sync just finished check if it is time to start a new sync - nextPendingSyncTime = maybeStartNextSyncLocked(); - break; - - case SyncHandler.MESSAGE_SERVICE_CONNECTED: { - ServiceConnectionData msgData = (ServiceConnectionData)msg.obj; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: " - + msgData.activeSyncContext); - } - // check that this isn't an old message - if (isSyncStillActive(msgData.activeSyncContext)) { - runBoundToSyncAdapter(msgData.activeSyncContext, msgData.syncAdapter); - } - break; - } - - case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: { - final ActiveSyncContext currentSyncContext = - ((ServiceConnectionData)msg.obj).activeSyncContext; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: " - + currentSyncContext); - } - // check that this isn't an old message - if (isSyncStillActive(currentSyncContext)) { - // cancel the sync if we have a syncadapter, which means one is - // outstanding - if (currentSyncContext.mSyncAdapter != null) { - try { - currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext); - } catch (RemoteException e) { - // we don't need to retry this in this case - } - } - - // pretend that the sync failed with an IOException, - // which is a soft error - SyncResult syncResult = new SyncResult(); - syncResult.stats.numIoExceptions++; - runSyncFinishedOrCanceledLocked(syncResult, currentSyncContext); - - // since a sync just finished check if it is time to start a new sync - nextPendingSyncTime = maybeStartNextSyncLocked(); - } - - break; - } - - case SyncHandler.MESSAGE_SYNC_ALARM: { - boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); - if (isLoggable) { - Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_ALARM"); - } - mAlarmScheduleTime = null; - try { - nextPendingSyncTime = maybeStartNextSyncLocked(); - } finally { - mHandleAlarmWakeLock.release(); - } - break; - } - - case SyncHandler.MESSAGE_CHECK_ALARMS: - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_CHECK_ALARMS"); - } - nextPendingSyncTime = maybeStartNextSyncLocked(); - break; - } - } finally { - manageSyncNotificationLocked(); - manageSyncAlarmLocked(earliestFuturePollTime, nextPendingSyncTime); - mSyncTimeTracker.update(); - mSyncManagerWakeLock.release(); - } - } - - /** - * Turn any periodic sync operations that are ready to run into pending sync operations. - * @return the desired start time of the earliest future periodic sync operation, - * in milliseconds since boot - */ - private long scheduleReadyPeriodicSyncs() { - final boolean backgroundDataUsageAllowed = - getConnectivityManager().getBackgroundDataSetting(); - long earliestFuturePollTime = Long.MAX_VALUE; - if (!backgroundDataUsageAllowed) { - return earliestFuturePollTime; - } - - AccountAndUser[] accounts = mRunningAccounts; - - final long nowAbsolute = System.currentTimeMillis(); - final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis) - ? (nowAbsolute - mSyncRandomOffsetMillis) : 0; - - ArrayList<SyncStorageEngine.AuthorityInfo> infos = mSyncStorageEngine.getAuthorities(); - for (SyncStorageEngine.AuthorityInfo info : infos) { - // skip the sync if the account of this operation no longer exists - if (!containsAccountAndUser(accounts, info.account, info.userId)) { - continue; - } - - if (!mSyncStorageEngine.getMasterSyncAutomatically(info.userId) - || !mSyncStorageEngine.getSyncAutomatically(info.account, info.userId, - info.authority)) { - continue; - } - - if (mSyncStorageEngine.getIsSyncable(info.account, info.userId, info.authority) - == 0) { - continue; - } - - SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(info); - for (int i = 0, N = info.periodicSyncs.size(); i < N; i++) { - final Bundle extras = info.periodicSyncs.get(i).first; - final Long periodInMillis = info.periodicSyncs.get(i).second * 1000; - // find when this periodic sync was last scheduled to run - final long lastPollTimeAbsolute = status.getPeriodicSyncTime(i); - - long remainingMillis - = periodInMillis - (shiftedNowAbsolute % periodInMillis); - - /* - * Sync scheduling strategy: - * Set the next periodic sync based on a random offset (in seconds). - * - * Also sync right now if any of the following cases hold - * and mark it as having been scheduled - * - * Case 1: This sync is ready to run now. - * Case 2: If the lastPollTimeAbsolute is in the future, - * sync now and reinitialize. This can happen for - * example if the user changed the time, synced and - * changed back. - * Case 3: If we failed to sync at the last scheduled time - */ - if (remainingMillis == periodInMillis // Case 1 - || lastPollTimeAbsolute > nowAbsolute // Case 2 - || (nowAbsolute - lastPollTimeAbsolute - >= periodInMillis)) { // Case 3 - // Sync now - final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff( - info.account, info.userId, info.authority); - final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; - syncAdapterInfo = mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey(info.authority, info.account.type), - info.userId); - if (syncAdapterInfo == null) { - continue; - } - scheduleSyncOperation( - new SyncOperation(info.account, info.userId, - SyncOperation.REASON_PERIODIC, - SyncStorageEngine.SOURCE_PERIODIC, - info.authority, extras, 0 /* delay */, - backoff != null ? backoff.first : 0, - mSyncStorageEngine.getDelayUntilTime( - info.account, info.userId, info.authority), - syncAdapterInfo.type.allowParallelSyncs())); - status.setPeriodicSyncTime(i, nowAbsolute); - } - // Compute when this periodic sync should next run - final long nextPollTimeAbsolute = nowAbsolute + remainingMillis; - - // remember this time if it is earlier than earliestFuturePollTime - if (nextPollTimeAbsolute < earliestFuturePollTime) { - earliestFuturePollTime = nextPollTimeAbsolute; - } - } - } - - if (earliestFuturePollTime == Long.MAX_VALUE) { - return Long.MAX_VALUE; - } - - // convert absolute time to elapsed time - return SystemClock.elapsedRealtime() - + ((earliestFuturePollTime < nowAbsolute) - ? 0 - : (earliestFuturePollTime - nowAbsolute)); - } - - private long maybeStartNextSyncLocked() { - final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); - if (isLoggable) Log.v(TAG, "maybeStartNextSync"); - - // If we aren't ready to run (e.g. the data connection is down), get out. - if (!mDataConnectionIsConnected) { - if (isLoggable) { - Log.v(TAG, "maybeStartNextSync: no data connection, skipping"); - } - return Long.MAX_VALUE; - } - - if (mStorageIsLow) { - if (isLoggable) { - Log.v(TAG, "maybeStartNextSync: memory low, skipping"); - } - return Long.MAX_VALUE; - } - - // If the accounts aren't known yet then we aren't ready to run. We will be kicked - // when the account lookup request does complete. - AccountAndUser[] accounts = mRunningAccounts; - if (accounts == INITIAL_ACCOUNTS_ARRAY) { - if (isLoggable) { - Log.v(TAG, "maybeStartNextSync: accounts not known, skipping"); - } - return Long.MAX_VALUE; - } - - // Otherwise consume SyncOperations from the head of the SyncQueue until one is - // found that is runnable (not disabled, etc). If that one is ready to run then - // start it, otherwise just get out. - final boolean backgroundDataUsageAllowed = - getConnectivityManager().getBackgroundDataSetting(); - - final long now = SystemClock.elapsedRealtime(); - - // will be set to the next time that a sync should be considered for running - long nextReadyToRunTime = Long.MAX_VALUE; - - // order the sync queue, dropping syncs that are not allowed - ArrayList<SyncOperation> operations = new ArrayList<SyncOperation>(); - synchronized (mSyncQueue) { - if (isLoggable) { - Log.v(TAG, "build the operation array, syncQueue size is " - + mSyncQueue.getOperations().size()); - } - final Iterator<SyncOperation> operationIterator = mSyncQueue.getOperations() - .iterator(); - - final ActivityManager activityManager - = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); - final Set<Integer> removedUsers = Sets.newHashSet(); - while (operationIterator.hasNext()) { - final SyncOperation op = operationIterator.next(); - - // drop the sync if the account of this operation no longer exists - if (!containsAccountAndUser(accounts, op.account, op.userId)) { - operationIterator.remove(); - mSyncStorageEngine.deleteFromPending(op.pendingOperation); - continue; - } - - // drop this sync request if it isn't syncable - int syncableState = mSyncStorageEngine.getIsSyncable( - op.account, op.userId, op.authority); - if (syncableState == 0) { - operationIterator.remove(); - mSyncStorageEngine.deleteFromPending(op.pendingOperation); - continue; - } - - // if the user in not running, drop the request - if (!activityManager.isUserRunning(op.userId)) { - final UserInfo userInfo = mUserManager.getUserInfo(op.userId); - if (userInfo == null) { - removedUsers.add(op.userId); - } - continue; - } - - // if the next run time is in the future, meaning there are no syncs ready - // to run, return the time - if (op.effectiveRunTime > now) { - if (nextReadyToRunTime > op.effectiveRunTime) { - nextReadyToRunTime = op.effectiveRunTime; - } - continue; - } - - final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; - syncAdapterInfo = mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey(op.authority, op.account.type), op.userId); - - // only proceed if network is connected for requesting UID - final boolean uidNetworkConnected; - if (syncAdapterInfo != null) { - final NetworkInfo networkInfo = getConnectivityManager() - .getActiveNetworkInfoForUid(syncAdapterInfo.uid); - uidNetworkConnected = networkInfo != null && networkInfo.isConnected(); - } else { - uidNetworkConnected = false; - } - - // skip the sync if it isn't manual, and auto sync or - // background data usage is disabled or network is - // disconnected for the target UID. - if (!op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false) - && (syncableState > 0) - && (!mSyncStorageEngine.getMasterSyncAutomatically(op.userId) - || !backgroundDataUsageAllowed - || !uidNetworkConnected - || !mSyncStorageEngine.getSyncAutomatically( - op.account, op.userId, op.authority))) { - operationIterator.remove(); - mSyncStorageEngine.deleteFromPending(op.pendingOperation); - continue; - } - - operations.add(op); - } - for (Integer user : removedUsers) { - // if it's still removed - if (mUserManager.getUserInfo(user) == null) { - onUserRemoved(user); - } - } - } - - // find the next operation to dispatch, if one is ready - // iterate from the top, keep issuing (while potentially cancelling existing syncs) - // until the quotas are filled. - // once the quotas are filled iterate once more to find when the next one would be - // (also considering pre-emption reasons). - if (isLoggable) Log.v(TAG, "sort the candidate operations, size " + operations.size()); - Collections.sort(operations); - if (isLoggable) Log.v(TAG, "dispatch all ready sync operations"); - for (int i = 0, N = operations.size(); i < N; i++) { - final SyncOperation candidate = operations.get(i); - final boolean candidateIsInitialization = candidate.isInitialization(); - - int numInit = 0; - int numRegular = 0; - ActiveSyncContext conflict = null; - ActiveSyncContext longRunning = null; - ActiveSyncContext toReschedule = null; - ActiveSyncContext oldestNonExpeditedRegular = null; - - for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) { - final SyncOperation activeOp = activeSyncContext.mSyncOperation; - if (activeOp.isInitialization()) { - numInit++; - } else { - numRegular++; - if (!activeOp.isExpedited()) { - if (oldestNonExpeditedRegular == null - || (oldestNonExpeditedRegular.mStartTime - > activeSyncContext.mStartTime)) { - oldestNonExpeditedRegular = activeSyncContext; - } - } - } - if (activeOp.account.type.equals(candidate.account.type) - && activeOp.authority.equals(candidate.authority) - && activeOp.userId == candidate.userId - && (!activeOp.allowParallelSyncs - || activeOp.account.name.equals(candidate.account.name))) { - conflict = activeSyncContext; - // don't break out since we want to do a full count of the varieties - } else { - if (candidateIsInitialization == activeOp.isInitialization() - && activeSyncContext.mStartTime + MAX_TIME_PER_SYNC < now) { - longRunning = activeSyncContext; - // don't break out since we want to do a full count of the varieties - } - } - } - - if (isLoggable) { - Log.v(TAG, "candidate " + (i + 1) + " of " + N + ": " + candidate); - Log.v(TAG, " numActiveInit=" + numInit + ", numActiveRegular=" + numRegular); - Log.v(TAG, " longRunning: " + longRunning); - Log.v(TAG, " conflict: " + conflict); - Log.v(TAG, " oldestNonExpeditedRegular: " + oldestNonExpeditedRegular); - } - - final boolean roomAvailable = candidateIsInitialization - ? numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS - : numRegular < MAX_SIMULTANEOUS_REGULAR_SYNCS; - - if (conflict != null) { - if (candidateIsInitialization && !conflict.mSyncOperation.isInitialization() - && numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS) { - toReschedule = conflict; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "canceling and rescheduling sync since an initialization " - + "takes higher priority, " + conflict); - } - } else if (candidate.expedited && !conflict.mSyncOperation.expedited - && (candidateIsInitialization - == conflict.mSyncOperation.isInitialization())) { - toReschedule = conflict; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "canceling and rescheduling sync since an expedited " - + "takes higher priority, " + conflict); - } - } else { - continue; - } - } else if (roomAvailable) { - // dispatch candidate - } else if (candidate.isExpedited() && oldestNonExpeditedRegular != null - && !candidateIsInitialization) { - // We found an active, non-expedited regular sync. We also know that the - // candidate doesn't conflict with this active sync since conflict - // is null. Reschedule the active sync and start the candidate. - toReschedule = oldestNonExpeditedRegular; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "canceling and rescheduling sync since an expedited is ready to run, " - + oldestNonExpeditedRegular); - } - } else if (longRunning != null - && (candidateIsInitialization - == longRunning.mSyncOperation.isInitialization())) { - // We found an active, long-running sync. Reschedule the active - // sync and start the candidate. - toReschedule = longRunning; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "canceling and rescheduling sync since it ran roo long, " - + longRunning); - } - } else { - // we were unable to find or make space to run this candidate, go on to - // the next one - continue; - } - - if (toReschedule != null) { - runSyncFinishedOrCanceledLocked(null, toReschedule); - scheduleSyncOperation(toReschedule.mSyncOperation); - } - synchronized (mSyncQueue) { - mSyncQueue.remove(candidate); - } - dispatchSyncOperation(candidate); - } - - return nextReadyToRunTime; - } - - private boolean dispatchSyncOperation(SyncOperation op) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "dispatchSyncOperation: we are going to sync " + op); - Log.v(TAG, "num active syncs: " + mActiveSyncContexts.size()); - for (ActiveSyncContext syncContext : mActiveSyncContexts) { - Log.v(TAG, syncContext.toString()); - } - } - - // connect to the sync adapter - SyncAdapterType syncAdapterType = SyncAdapterType.newKey(op.authority, op.account.type); - final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; - syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, op.userId); - if (syncAdapterInfo == null) { - Log.d(TAG, "can't find a sync adapter for " + syncAdapterType - + ", removing settings for it"); - mSyncStorageEngine.removeAuthority(op.account, op.userId, op.authority); - return false; - } - - ActiveSyncContext activeSyncContext = - new ActiveSyncContext(op, insertStartSyncEvent(op), syncAdapterInfo.uid); - activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext); - mActiveSyncContexts.add(activeSyncContext); - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext); - } - if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo, op.userId)) { - Log.e(TAG, "Bind attempt failed to " + syncAdapterInfo); - closeActiveSyncContext(activeSyncContext); - return false; - } - - return true; - } - - private void runBoundToSyncAdapter(final ActiveSyncContext activeSyncContext, - ISyncAdapter syncAdapter) { - activeSyncContext.mSyncAdapter = syncAdapter; - final SyncOperation syncOperation = activeSyncContext.mSyncOperation; - try { - activeSyncContext.mIsLinkedToDeath = true; - syncAdapter.asBinder().linkToDeath(activeSyncContext, 0); - - syncAdapter.startSync(activeSyncContext, syncOperation.authority, - syncOperation.account, syncOperation.extras); - } catch (RemoteException remoteExc) { - Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc); - closeActiveSyncContext(activeSyncContext); - increaseBackoffSetting(syncOperation); - scheduleSyncOperation(new SyncOperation(syncOperation)); - } catch (RuntimeException exc) { - closeActiveSyncContext(activeSyncContext); - Log.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc); - } - } - - private void cancelActiveSyncLocked(Account account, int userId, String authority) { - ArrayList<ActiveSyncContext> activeSyncs = - new ArrayList<ActiveSyncContext>(mActiveSyncContexts); - for (ActiveSyncContext activeSyncContext : activeSyncs) { - if (activeSyncContext != null) { - // if an account was specified then only cancel the sync if it matches - if (account != null) { - if (!account.equals(activeSyncContext.mSyncOperation.account)) { - continue; - } - } - // if an authority was specified then only cancel the sync if it matches - if (authority != null) { - if (!authority.equals(activeSyncContext.mSyncOperation.authority)) { - continue; - } - } - // check if the userid matches - if (userId != UserHandle.USER_ALL - && userId != activeSyncContext.mSyncOperation.userId) { - continue; - } - runSyncFinishedOrCanceledLocked(null /* no result since this is a cancel */, - activeSyncContext); - } - } - } - - private void runSyncFinishedOrCanceledLocked(SyncResult syncResult, - ActiveSyncContext activeSyncContext) { - boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); - - if (activeSyncContext.mIsLinkedToDeath) { - activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0); - activeSyncContext.mIsLinkedToDeath = false; - } - closeActiveSyncContext(activeSyncContext); - - final SyncOperation syncOperation = activeSyncContext.mSyncOperation; - - final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime; - - String historyMessage; - int downstreamActivity; - int upstreamActivity; - if (syncResult != null) { - if (isLoggable) { - Log.v(TAG, "runSyncFinishedOrCanceled [finished]: " - + syncOperation + ", result " + syncResult); - } - - if (!syncResult.hasError()) { - historyMessage = SyncStorageEngine.MESG_SUCCESS; - // TODO: set these correctly when the SyncResult is extended to include it - downstreamActivity = 0; - upstreamActivity = 0; - clearBackoffSetting(syncOperation); - } else { - Log.d(TAG, "failed sync operation " + syncOperation + ", " + syncResult); - // the operation failed so increase the backoff time - if (!syncResult.syncAlreadyInProgress) { - increaseBackoffSetting(syncOperation); - } - // reschedule the sync if so indicated by the syncResult - maybeRescheduleSync(syncResult, syncOperation); - historyMessage = ContentResolver.syncErrorToString( - syncResultToErrorNumber(syncResult)); - // TODO: set these correctly when the SyncResult is extended to include it - downstreamActivity = 0; - upstreamActivity = 0; - } - - setDelayUntilTime(syncOperation, syncResult.delayUntil); - } else { - if (isLoggable) { - Log.v(TAG, "runSyncFinishedOrCanceled [canceled]: " + syncOperation); - } - if (activeSyncContext.mSyncAdapter != null) { - try { - activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext); - } catch (RemoteException e) { - // we don't need to retry this in this case - } - } - historyMessage = SyncStorageEngine.MESG_CANCELED; - downstreamActivity = 0; - upstreamActivity = 0; - } - - stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage, - upstreamActivity, downstreamActivity, elapsedTime); - - if (syncResult != null && syncResult.tooManyDeletions) { - installHandleTooManyDeletesNotification(syncOperation.account, - syncOperation.authority, syncResult.stats.numDeletes, - syncOperation.userId); - } else { - mNotificationMgr.cancelAsUser(null, - syncOperation.account.hashCode() ^ syncOperation.authority.hashCode(), - new UserHandle(syncOperation.userId)); - } - - if (syncResult != null && syncResult.fullSyncRequested) { - scheduleSyncOperation(new SyncOperation(syncOperation.account, syncOperation.userId, - syncOperation.reason, - syncOperation.syncSource, syncOperation.authority, new Bundle(), 0, - syncOperation.backoff, syncOperation.delayUntil, - syncOperation.allowParallelSyncs)); - } - // no need to schedule an alarm, as that will be done by our caller. - } - - private void closeActiveSyncContext(ActiveSyncContext activeSyncContext) { - activeSyncContext.close(); - mActiveSyncContexts.remove(activeSyncContext); - mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo, - activeSyncContext.mSyncOperation.userId); - } - - /** - * Convert the error-containing SyncResult into the Sync.History error number. Since - * the SyncResult may indicate multiple errors at once, this method just returns the - * most "serious" error. - * @param syncResult the SyncResult from which to read - * @return the most "serious" error set in the SyncResult - * @throws IllegalStateException if the SyncResult does not indicate any errors. - * If SyncResult.error() is true then it is safe to call this. - */ - private int syncResultToErrorNumber(SyncResult syncResult) { - if (syncResult.syncAlreadyInProgress) - return ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS; - if (syncResult.stats.numAuthExceptions > 0) - return ContentResolver.SYNC_ERROR_AUTHENTICATION; - if (syncResult.stats.numIoExceptions > 0) - return ContentResolver.SYNC_ERROR_IO; - if (syncResult.stats.numParseExceptions > 0) - return ContentResolver.SYNC_ERROR_PARSE; - if (syncResult.stats.numConflictDetectedExceptions > 0) - return ContentResolver.SYNC_ERROR_CONFLICT; - if (syncResult.tooManyDeletions) - return ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS; - if (syncResult.tooManyRetries) - return ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES; - if (syncResult.databaseError) - return ContentResolver.SYNC_ERROR_INTERNAL; - throw new IllegalStateException("we are not in an error state, " + syncResult); - } - - private void manageSyncNotificationLocked() { - boolean shouldCancel; - boolean shouldInstall; - - if (mActiveSyncContexts.isEmpty()) { - mSyncNotificationInfo.startTime = null; - - // we aren't syncing. if the notification is active then remember that we need - // to cancel it and then clear out the info - shouldCancel = mSyncNotificationInfo.isActive; - shouldInstall = false; - } else { - // we are syncing - final long now = SystemClock.elapsedRealtime(); - if (mSyncNotificationInfo.startTime == null) { - mSyncNotificationInfo.startTime = now; - } - - // there are three cases: - // - the notification is up: do nothing - // - the notification is not up but it isn't time yet: don't install - // - the notification is not up and it is time: need to install - - if (mSyncNotificationInfo.isActive) { - shouldInstall = shouldCancel = false; - } else { - // it isn't currently up, so there is nothing to cancel - shouldCancel = false; - - final boolean timeToShowNotification = - now > mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY; - if (timeToShowNotification) { - shouldInstall = true; - } else { - // show the notification immediately if this is a manual sync - shouldInstall = false; - for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) { - final boolean manualSync = activeSyncContext.mSyncOperation.extras - .getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false); - if (manualSync) { - shouldInstall = true; - break; - } - } - } - } - } - - if (shouldCancel && !shouldInstall) { - mNeedSyncActiveNotification = false; - sendSyncStateIntent(); - mSyncNotificationInfo.isActive = false; - } - - if (shouldInstall) { - mNeedSyncActiveNotification = true; - sendSyncStateIntent(); - mSyncNotificationInfo.isActive = true; - } - } - - private void manageSyncAlarmLocked(long nextPeriodicEventElapsedTime, - long nextPendingEventElapsedTime) { - // in each of these cases the sync loop will be kicked, which will cause this - // method to be called again - if (!mDataConnectionIsConnected) return; - if (mStorageIsLow) return; - - // When the status bar notification should be raised - final long notificationTime = - (!mSyncHandler.mSyncNotificationInfo.isActive - && mSyncHandler.mSyncNotificationInfo.startTime != null) - ? mSyncHandler.mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY - : Long.MAX_VALUE; - - // When we should consider canceling an active sync - long earliestTimeoutTime = Long.MAX_VALUE; - for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) { - final long currentSyncTimeoutTime = - currentSyncContext.mTimeoutStartTime + MAX_TIME_PER_SYNC; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "manageSyncAlarm: active sync, mTimeoutStartTime + MAX is " - + currentSyncTimeoutTime); - } - if (earliestTimeoutTime > currentSyncTimeoutTime) { - earliestTimeoutTime = currentSyncTimeoutTime; - } - } - - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "manageSyncAlarm: notificationTime is " + notificationTime); - } - - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "manageSyncAlarm: earliestTimeoutTime is " + earliestTimeoutTime); - } - - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "manageSyncAlarm: nextPeriodicEventElapsedTime is " - + nextPeriodicEventElapsedTime); - } - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "manageSyncAlarm: nextPendingEventElapsedTime is " - + nextPendingEventElapsedTime); - } - - long alarmTime = Math.min(notificationTime, earliestTimeoutTime); - alarmTime = Math.min(alarmTime, nextPeriodicEventElapsedTime); - alarmTime = Math.min(alarmTime, nextPendingEventElapsedTime); - - // Bound the alarm time. - final long now = SystemClock.elapsedRealtime(); - if (alarmTime < now + SYNC_ALARM_TIMEOUT_MIN) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "manageSyncAlarm: the alarmTime is too small, " - + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN)); - } - alarmTime = now + SYNC_ALARM_TIMEOUT_MIN; - } else if (alarmTime > now + SYNC_ALARM_TIMEOUT_MAX) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "manageSyncAlarm: the alarmTime is too large, " - + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN)); - } - alarmTime = now + SYNC_ALARM_TIMEOUT_MAX; - } - - // determine if we need to set or cancel the alarm - boolean shouldSet = false; - boolean shouldCancel = false; - final boolean alarmIsActive = mAlarmScheduleTime != null; - final boolean needAlarm = alarmTime != Long.MAX_VALUE; - if (needAlarm) { - if (!alarmIsActive || alarmTime < mAlarmScheduleTime) { - shouldSet = true; - } - } else { - shouldCancel = alarmIsActive; - } - - // set or cancel the alarm as directed - ensureAlarmService(); - if (shouldSet) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "requesting that the alarm manager wake us up at elapsed time " - + alarmTime + ", now is " + now + ", " + ((alarmTime - now) / 1000) - + " secs from now"); - } - mAlarmScheduleTime = alarmTime; - mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime, - mSyncAlarmIntent); - } else if (shouldCancel) { - mAlarmScheduleTime = null; - mAlarmService.cancel(mSyncAlarmIntent); - } - } - - private void sendSyncStateIntent() { - Intent syncStateIntent = new Intent(Intent.ACTION_SYNC_STATE_CHANGED); - syncStateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - syncStateIntent.putExtra("active", mNeedSyncActiveNotification); - syncStateIntent.putExtra("failing", false); - mContext.sendBroadcastAsUser(syncStateIntent, UserHandle.OWNER); - } - - private void installHandleTooManyDeletesNotification(Account account, String authority, - long numDeletes, int userId) { - if (mNotificationMgr == null) return; - - final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider( - authority, 0 /* flags */); - if (providerInfo == null) { - return; - } - CharSequence authorityName = providerInfo.loadLabel(mContext.getPackageManager()); - - Intent clickIntent = new Intent(mContext, SyncActivityTooManyDeletes.class); - clickIntent.putExtra("account", account); - clickIntent.putExtra("authority", authority); - clickIntent.putExtra("provider", authorityName.toString()); - clickIntent.putExtra("numDeletes", numDeletes); - - if (!isActivityAvailable(clickIntent)) { - Log.w(TAG, "No activity found to handle too many deletes."); - return; - } - - final PendingIntent pendingIntent = PendingIntent - .getActivityAsUser(mContext, 0, clickIntent, - PendingIntent.FLAG_CANCEL_CURRENT, null, new UserHandle(userId)); - - CharSequence tooManyDeletesDescFormat = mContext.getResources().getText( - R.string.contentServiceTooManyDeletesNotificationDesc); - - Notification notification = - new Notification(R.drawable.stat_notify_sync_error, - mContext.getString(R.string.contentServiceSync), - System.currentTimeMillis()); - notification.setLatestEventInfo(mContext, - mContext.getString(R.string.contentServiceSyncNotificationTitle), - String.format(tooManyDeletesDescFormat.toString(), authorityName), - pendingIntent); - notification.flags |= Notification.FLAG_ONGOING_EVENT; - mNotificationMgr.notifyAsUser(null, account.hashCode() ^ authority.hashCode(), - notification, new UserHandle(userId)); - } - - /** - * Checks whether an activity exists on the system image for the given intent. - * - * @param intent The intent for an activity. - * @return Whether or not an activity exists. - */ - private boolean isActivityAvailable(Intent intent) { - PackageManager pm = mContext.getPackageManager(); - List<ResolveInfo> list = pm.queryIntentActivities(intent, 0); - int listSize = list.size(); - for (int i = 0; i < listSize; i++) { - ResolveInfo resolveInfo = list.get(i); - if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) - != 0) { - return true; - } - } - - return false; - } - - public long insertStartSyncEvent(SyncOperation syncOperation) { - final int source = syncOperation.syncSource; - final long now = System.currentTimeMillis(); - - EventLog.writeEvent(2720, syncOperation.authority, - SyncStorageEngine.EVENT_START, source, - syncOperation.account.name.hashCode()); - - return mSyncStorageEngine.insertStartSyncEvent( - syncOperation.account, syncOperation.userId, syncOperation.reason, - syncOperation.authority, - now, source, syncOperation.isInitialization(), syncOperation.extras - ); - } - - public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage, - int upstreamActivity, int downstreamActivity, long elapsedTime) { - EventLog.writeEvent(2720, syncOperation.authority, - SyncStorageEngine.EVENT_STOP, syncOperation.syncSource, - syncOperation.account.name.hashCode()); - - mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime, - resultMessage, downstreamActivity, upstreamActivity); - } - } - - private boolean isSyncStillActive(ActiveSyncContext activeSyncContext) { - for (ActiveSyncContext sync : mActiveSyncContexts) { - if (sync == activeSyncContext) { - return true; - } - } - return false; - } - - static class PrintTable { - private ArrayList<Object[]> mTable = Lists.newArrayList(); - private final int mCols; - - PrintTable(int cols) { - mCols = cols; - } - - void set(int row, int col, Object... values) { - if (col + values.length > mCols) { - throw new IndexOutOfBoundsException("Table only has " + mCols + - " columns. can't set " + values.length + " at column " + col); - } - for (int i = mTable.size(); i <= row; i++) { - final Object[] list = new Object[mCols]; - mTable.add(list); - for (int j = 0; j < mCols; j++) { - list[j] = ""; - } - } - System.arraycopy(values, 0, mTable.get(row), col, values.length); - } - - void writeTo(PrintWriter out) { - final String[] formats = new String[mCols]; - int totalLength = 0; - for (int col = 0; col < mCols; ++col) { - int maxLength = 0; - for (Object[] row : mTable) { - final int length = row[col].toString().length(); - if (length > maxLength) { - maxLength = length; - } - } - totalLength += maxLength; - formats[col] = String.format("%%-%ds", maxLength); - } - printRow(out, formats, mTable.get(0)); - totalLength += (mCols - 1) * 2; - for (int i = 0; i < totalLength; ++i) { - out.print("-"); - } - out.println(); - for (int i = 1, mTableSize = mTable.size(); i < mTableSize; i++) { - Object[] row = mTable.get(i); - printRow(out, formats, row); - } - } - - private void printRow(PrintWriter out, String[] formats, Object[] row) { - for (int j = 0, rowLength = row.length; j < rowLength; j++) { - out.printf(String.format(formats[j], row[j].toString())); - out.print(" "); - } - out.println(); - } - - public int getNumRows() { - return mTable.size(); - } - } -} diff --git a/core/java/android/content/SyncOperation.java b/core/java/android/content/SyncOperation.java deleted file mode 100644 index a4c2cff..0000000 --- a/core/java/android/content/SyncOperation.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content; - -import android.accounts.Account; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.os.SystemClock; - -/** - * Value type that represents a sync operation. - * @hide - */ -public class SyncOperation implements Comparable { - public static final int REASON_BACKGROUND_DATA_SETTINGS_CHANGED = -1; - public static final int REASON_ACCOUNTS_UPDATED = -2; - public static final int REASON_SERVICE_CHANGED = -3; - public static final int REASON_PERIODIC = -4; - public static final int REASON_IS_SYNCABLE = -5; - public static final int REASON_SYNC_AUTO = -6; - public static final int REASON_MASTER_SYNC_AUTO = -7; - public static final int REASON_USER_START = -8; - - private static String[] REASON_NAMES = new String[] { - "DataSettingsChanged", - "AccountsUpdated", - "ServiceChanged", - "Periodic", - "IsSyncable", - "AutoSync", - "MasterSyncAuto", - "UserStart", - }; - - public final Account account; - public final int userId; - public final int reason; - public int syncSource; - public String authority; - public final boolean allowParallelSyncs; - public Bundle extras; - public final String key; - public long earliestRunTime; - public boolean expedited; - public SyncStorageEngine.PendingOperation pendingOperation; - public Long backoff; - public long delayUntil; - public long effectiveRunTime; - - public SyncOperation(Account account, int userId, int reason, int source, String authority, - Bundle extras, long delayInMs, long backoff, long delayUntil, - boolean allowParallelSyncs) { - this.account = account; - this.userId = userId; - this.reason = reason; - this.syncSource = source; - this.authority = authority; - this.allowParallelSyncs = allowParallelSyncs; - this.extras = new Bundle(extras); - removeFalseExtra(ContentResolver.SYNC_EXTRAS_UPLOAD); - removeFalseExtra(ContentResolver.SYNC_EXTRAS_MANUAL); - removeFalseExtra(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS); - removeFalseExtra(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF); - removeFalseExtra(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY); - removeFalseExtra(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS); - removeFalseExtra(ContentResolver.SYNC_EXTRAS_EXPEDITED); - removeFalseExtra(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS); - this.delayUntil = delayUntil; - this.backoff = backoff; - final long now = SystemClock.elapsedRealtime(); - if (delayInMs < 0) { - this.expedited = true; - this.earliestRunTime = now; - } else { - this.expedited = false; - this.earliestRunTime = now + delayInMs; - } - updateEffectiveRunTime(); - this.key = toKey(); - } - - private void removeFalseExtra(String extraName) { - if (!extras.getBoolean(extraName, false)) { - extras.remove(extraName); - } - } - - SyncOperation(SyncOperation other) { - this.account = other.account; - this.userId = other.userId; - this.reason = other.reason; - this.syncSource = other.syncSource; - this.authority = other.authority; - this.extras = new Bundle(other.extras); - this.expedited = other.expedited; - this.earliestRunTime = SystemClock.elapsedRealtime(); - this.backoff = other.backoff; - this.delayUntil = other.delayUntil; - this.allowParallelSyncs = other.allowParallelSyncs; - this.updateEffectiveRunTime(); - this.key = toKey(); - } - - public String toString() { - return dump(null, true); - } - - public String dump(PackageManager pm, boolean useOneLine) { - StringBuilder sb = new StringBuilder() - .append(account.name) - .append(" u") - .append(userId).append(" (") - .append(account.type) - .append(")") - .append(", ") - .append(authority) - .append(", ") - .append(SyncStorageEngine.SOURCES[syncSource]) - .append(", earliestRunTime ") - .append(earliestRunTime); - if (expedited) { - sb.append(", EXPEDITED"); - } - sb.append(", reason: "); - sb.append(reasonToString(pm, reason)); - if (!useOneLine && !extras.keySet().isEmpty()) { - sb.append("\n "); - extrasToStringBuilder(extras, sb); - } - return sb.toString(); - } - - public static String reasonToString(PackageManager pm, int reason) { - if (reason >= 0) { - if (pm != null) { - final String[] packages = pm.getPackagesForUid(reason); - if (packages != null && packages.length == 1) { - return packages[0]; - } - final String name = pm.getNameForUid(reason); - if (name != null) { - return name; - } - return String.valueOf(reason); - } else { - return String.valueOf(reason); - } - } else { - final int index = -reason - 1; - if (index >= REASON_NAMES.length) { - return String.valueOf(reason); - } else { - return REASON_NAMES[index]; - } - } - } - - public boolean isInitialization() { - return extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false); - } - - public boolean isExpedited() { - return extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false); - } - - public boolean ignoreBackoff() { - return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false); - } - - private String toKey() { - StringBuilder sb = new StringBuilder(); - sb.append("authority: ").append(authority); - sb.append(" account {name=" + account.name + ", user=" + userId + ", type=" + account.type - + "}"); - sb.append(" extras: "); - extrasToStringBuilder(extras, sb); - return sb.toString(); - } - - public static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) { - sb.append("["); - for (String key : bundle.keySet()) { - sb.append(key).append("=").append(bundle.get(key)).append(" "); - } - sb.append("]"); - } - - public void updateEffectiveRunTime() { - effectiveRunTime = ignoreBackoff() - ? earliestRunTime - : Math.max( - Math.max(earliestRunTime, delayUntil), - backoff); - } - - public int compareTo(Object o) { - SyncOperation other = (SyncOperation)o; - - if (expedited != other.expedited) { - return expedited ? -1 : 1; - } - - if (effectiveRunTime == other.effectiveRunTime) { - return 0; - } - - return effectiveRunTime < other.effectiveRunTime ? -1 : 1; - } -} diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java deleted file mode 100644 index c09703c..0000000 --- a/core/java/android/content/SyncQueue.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content; - -import android.accounts.Account; -import android.content.pm.PackageManager; -import android.content.pm.RegisteredServicesCache; -import android.content.pm.RegisteredServicesCache.ServiceInfo; -import android.os.SystemClock; -import android.os.UserHandle; -import android.text.format.DateUtils; -import android.util.Log; -import android.util.Pair; - -import com.google.android.collect.Maps; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -/** - * Queue of pending sync operations. Not inherently thread safe, external - * callers are responsible for locking. - * - * @hide - */ -public class SyncQueue { - private static final String TAG = "SyncManager"; - private final SyncStorageEngine mSyncStorageEngine; - private final SyncAdaptersCache mSyncAdapters; - private final PackageManager mPackageManager; - - // A Map of SyncOperations operationKey -> SyncOperation that is designed for - // quick lookup of an enqueued SyncOperation. - private final HashMap<String, SyncOperation> mOperationsMap = Maps.newHashMap(); - - public SyncQueue(PackageManager packageManager, SyncStorageEngine syncStorageEngine, - final SyncAdaptersCache syncAdapters) { - mPackageManager = packageManager; - mSyncStorageEngine = syncStorageEngine; - mSyncAdapters = syncAdapters; - } - - public void addPendingOperations(int userId) { - for (SyncStorageEngine.PendingOperation op : mSyncStorageEngine.getPendingOperations()) { - if (op.userId != userId) continue; - - final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff( - op.account, op.userId, op.authority); - final ServiceInfo<SyncAdapterType> syncAdapterInfo = mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey(op.authority, op.account.type), op.userId); - if (syncAdapterInfo == null) { - Log.w(TAG, "Missing sync adapter info for authority " + op.authority + ", userId " - + op.userId); - continue; - } - SyncOperation syncOperation = new SyncOperation( - op.account, op.userId, op.reason, op.syncSource, op.authority, op.extras, - 0 /* delay */, backoff != null ? backoff.first : 0, - mSyncStorageEngine.getDelayUntilTime(op.account, op.userId, op.authority), - syncAdapterInfo.type.allowParallelSyncs()); - syncOperation.expedited = op.expedited; - syncOperation.pendingOperation = op; - add(syncOperation, op); - } - } - - public boolean add(SyncOperation operation) { - return add(operation, null /* this is not coming from the database */); - } - - private boolean add(SyncOperation operation, - SyncStorageEngine.PendingOperation pop) { - // - if an operation with the same key exists and this one should run earlier, - // update the earliestRunTime of the existing to the new time - // - if an operation with the same key exists and if this one should run - // later, ignore it - // - if no operation exists then add the new one - final String operationKey = operation.key; - final SyncOperation existingOperation = mOperationsMap.get(operationKey); - - if (existingOperation != null) { - boolean changed = false; - if (existingOperation.expedited == operation.expedited) { - final long newRunTime = - Math.min(existingOperation.earliestRunTime, operation.earliestRunTime); - if (existingOperation.earliestRunTime != newRunTime) { - existingOperation.earliestRunTime = newRunTime; - changed = true; - } - } else { - if (operation.expedited) { - existingOperation.expedited = true; - changed = true; - } - } - return changed; - } - - operation.pendingOperation = pop; - if (operation.pendingOperation == null) { - pop = new SyncStorageEngine.PendingOperation( - operation.account, operation.userId, operation.reason, operation.syncSource, - operation.authority, operation.extras, operation.expedited); - pop = mSyncStorageEngine.insertIntoPending(pop); - if (pop == null) { - throw new IllegalStateException("error adding pending sync operation " - + operation); - } - operation.pendingOperation = pop; - } - - mOperationsMap.put(operationKey, operation); - return true; - } - - public void removeUser(int userId) { - ArrayList<SyncOperation> opsToRemove = new ArrayList<SyncOperation>(); - for (SyncOperation op : mOperationsMap.values()) { - if (op.userId == userId) { - opsToRemove.add(op); - } - } - - for (SyncOperation op : opsToRemove) { - remove(op); - } - } - - /** - * Remove the specified operation if it is in the queue. - * @param operation the operation to remove - */ - public void remove(SyncOperation operation) { - SyncOperation operationToRemove = mOperationsMap.remove(operation.key); - if (operationToRemove == null) { - return; - } - if (!mSyncStorageEngine.deleteFromPending(operationToRemove.pendingOperation)) { - final String errorMessage = "unable to find pending row for " + operationToRemove; - Log.e(TAG, errorMessage, new IllegalStateException(errorMessage)); - } - } - - public void onBackoffChanged(Account account, int userId, String providerName, long backoff) { - // for each op that matches the account and provider update its - // backoff and effectiveStartTime - for (SyncOperation op : mOperationsMap.values()) { - if (op.account.equals(account) && op.authority.equals(providerName) - && op.userId == userId) { - op.backoff = backoff; - op.updateEffectiveRunTime(); - } - } - } - - public void onDelayUntilTimeChanged(Account account, String providerName, long delayUntil) { - // for each op that matches the account and provider update its - // delayUntilTime and effectiveStartTime - for (SyncOperation op : mOperationsMap.values()) { - if (op.account.equals(account) && op.authority.equals(providerName)) { - op.delayUntil = delayUntil; - op.updateEffectiveRunTime(); - } - } - } - - public void remove(Account account, int userId, String authority) { - Iterator<Map.Entry<String, SyncOperation>> entries = mOperationsMap.entrySet().iterator(); - while (entries.hasNext()) { - Map.Entry<String, SyncOperation> entry = entries.next(); - SyncOperation syncOperation = entry.getValue(); - if (account != null && !syncOperation.account.equals(account)) { - continue; - } - if (authority != null && !syncOperation.authority.equals(authority)) { - continue; - } - if (userId != syncOperation.userId) { - continue; - } - entries.remove(); - if (!mSyncStorageEngine.deleteFromPending(syncOperation.pendingOperation)) { - final String errorMessage = "unable to find pending row for " + syncOperation; - Log.e(TAG, errorMessage, new IllegalStateException(errorMessage)); - } - } - } - - public Collection<SyncOperation> getOperations() { - return mOperationsMap.values(); - } - - public void dump(StringBuilder sb) { - final long now = SystemClock.elapsedRealtime(); - sb.append("SyncQueue: ").append(mOperationsMap.size()).append(" operation(s)\n"); - for (SyncOperation operation : mOperationsMap.values()) { - sb.append(" "); - if (operation.effectiveRunTime <= now) { - sb.append("READY"); - } else { - sb.append(DateUtils.formatElapsedTime((operation.effectiveRunTime - now) / 1000)); - } - sb.append(" - "); - sb.append(operation.dump(mPackageManager, false)).append("\n"); - } - } -} diff --git a/core/java/android/content/SyncStatusInfo.java b/core/java/android/content/SyncStatusInfo.java index bb2b2da..49e3e35 100644 --- a/core/java/android/content/SyncStatusInfo.java +++ b/core/java/android/content/SyncStatusInfo.java @@ -46,7 +46,7 @@ public class SyncStatusInfo implements Parcelable { private static final String TAG = "Sync"; - SyncStatusInfo(int authorityId) { + public SyncStatusInfo(int authorityId) { this.authorityId = authorityId; } @@ -92,7 +92,7 @@ public class SyncStatusInfo implements Parcelable { } } - SyncStatusInfo(Parcel parcel) { + public SyncStatusInfo(Parcel parcel) { int version = parcel.readInt(); if (version != VERSION && version != 1) { Log.w("SyncStatusInfo", "Unknown version: " + version); diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java deleted file mode 100644 index 8d9b8e0..0000000 --- a/core/java/android/content/SyncStorageEngine.java +++ /dev/null @@ -1,2318 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.FastXmlSerializer; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -import android.accounts.Account; -import android.accounts.AccountAndUser; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; -import android.database.sqlite.SQLiteQueryBuilder; -import android.os.Bundle; -import android.os.Environment; -import android.os.Handler; -import android.os.Message; -import android.os.Parcel; -import android.os.RemoteCallbackList; -import android.os.RemoteException; -import android.util.AtomicFile; -import android.util.Log; -import android.util.SparseArray; -import android.util.Xml; -import android.util.Pair; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Random; -import java.util.TimeZone; -import java.util.List; - -/** - * Singleton that tracks the sync data and overall sync - * history on the device. - * - * @hide - */ -public class SyncStorageEngine extends Handler { - - private static final String TAG = "SyncManager"; - private static final boolean DEBUG = false; - private static final boolean DEBUG_FILE = false; - - private static final String XML_ATTR_NEXT_AUTHORITY_ID = "nextAuthorityId"; - private static final String XML_ATTR_LISTEN_FOR_TICKLES = "listen-for-tickles"; - private static final String XML_ATTR_SYNC_RANDOM_OFFSET = "offsetInSeconds"; - private static final String XML_ATTR_ENABLED = "enabled"; - private static final String XML_ATTR_USER = "user"; - private static final String XML_TAG_LISTEN_FOR_TICKLES = "listenForTickles"; - - private static final long DEFAULT_POLL_FREQUENCY_SECONDS = 60 * 60 * 24; // One day - - @VisibleForTesting - static final long MILLIS_IN_4WEEKS = 1000L * 60 * 60 * 24 * 7 * 4; - - /** Enum value for a sync start event. */ - public static final int EVENT_START = 0; - - /** Enum value for a sync stop event. */ - public static final int EVENT_STOP = 1; - - // TODO: i18n -- grab these out of resources. - /** String names for the sync event types. */ - public static final String[] EVENTS = { "START", "STOP" }; - - /** Enum value for a server-initiated sync. */ - public static final int SOURCE_SERVER = 0; - - /** Enum value for a local-initiated sync. */ - public static final int SOURCE_LOCAL = 1; - /** - * Enum value for a poll-based sync (e.g., upon connection to - * network) - */ - public static final int SOURCE_POLL = 2; - - /** Enum value for a user-initiated sync. */ - public static final int SOURCE_USER = 3; - - /** Enum value for a periodic sync. */ - public static final int SOURCE_PERIODIC = 4; - - public static final long NOT_IN_BACKOFF_MODE = -1; - - public static final Intent SYNC_CONNECTION_SETTING_CHANGED_INTENT = - new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED"); - - // TODO: i18n -- grab these out of resources. - /** String names for the sync source types. */ - public static final String[] SOURCES = { "SERVER", - "LOCAL", - "POLL", - "USER", - "PERIODIC" }; - - // The MESG column will contain one of these or one of the Error types. - public static final String MESG_SUCCESS = "success"; - public static final String MESG_CANCELED = "canceled"; - - public static final int MAX_HISTORY = 100; - - private static final int MSG_WRITE_STATUS = 1; - private static final long WRITE_STATUS_DELAY = 1000*60*10; // 10 minutes - - private static final int MSG_WRITE_STATISTICS = 2; - private static final long WRITE_STATISTICS_DELAY = 1000*60*30; // 1/2 hour - - private static final boolean SYNC_ENABLED_DEFAULT = false; - - // the version of the accounts xml file format - private static final int ACCOUNTS_VERSION = 2; - - private static HashMap<String, String> sAuthorityRenames; - - static { - sAuthorityRenames = new HashMap<String, String>(); - sAuthorityRenames.put("contacts", "com.android.contacts"); - sAuthorityRenames.put("calendar", "com.android.calendar"); - } - - public static class PendingOperation { - final Account account; - final int userId; - final int reason; - final int syncSource; - final String authority; - final Bundle extras; // note: read-only. - final boolean expedited; - - int authorityId; - byte[] flatExtras; - - PendingOperation(Account account, int userId, int reason,int source, - String authority, Bundle extras, boolean expedited) { - this.account = account; - this.userId = userId; - this.syncSource = source; - this.reason = reason; - this.authority = authority; - this.extras = extras != null ? new Bundle(extras) : extras; - this.expedited = expedited; - this.authorityId = -1; - } - - PendingOperation(PendingOperation other) { - this.account = other.account; - this.userId = other.userId; - this.reason = other.reason; - this.syncSource = other.syncSource; - this.authority = other.authority; - this.extras = other.extras; - this.authorityId = other.authorityId; - this.expedited = other.expedited; - } - } - - static class AccountInfo { - final AccountAndUser accountAndUser; - final HashMap<String, AuthorityInfo> authorities = - new HashMap<String, AuthorityInfo>(); - - AccountInfo(AccountAndUser accountAndUser) { - this.accountAndUser = accountAndUser; - } - } - - public static class AuthorityInfo { - final Account account; - final int userId; - final String authority; - final int ident; - boolean enabled; - int syncable; - long backoffTime; - long backoffDelay; - long delayUntil; - final ArrayList<Pair<Bundle, Long>> periodicSyncs; - - /** - * Copy constructor for making deep-ish copies. Only the bundles stored - * in periodic syncs can make unexpected changes. - * - * @param toCopy AuthorityInfo to be copied. - */ - AuthorityInfo(AuthorityInfo toCopy) { - account = toCopy.account; - userId = toCopy.userId; - authority = toCopy.authority; - ident = toCopy.ident; - enabled = toCopy.enabled; - syncable = toCopy.syncable; - backoffTime = toCopy.backoffTime; - backoffDelay = toCopy.backoffDelay; - delayUntil = toCopy.delayUntil; - periodicSyncs = new ArrayList<Pair<Bundle, Long>>(); - for (Pair<Bundle, Long> sync : toCopy.periodicSyncs) { - // Still not a perfect copy, because we are just copying the mappings. - periodicSyncs.add(Pair.create(new Bundle(sync.first), sync.second)); - } - } - - AuthorityInfo(Account account, int userId, String authority, int ident) { - this.account = account; - this.userId = userId; - this.authority = authority; - this.ident = ident; - enabled = SYNC_ENABLED_DEFAULT; - syncable = -1; // default to "unknown" - backoffTime = -1; // if < 0 then we aren't in backoff mode - backoffDelay = -1; // if < 0 then we aren't in backoff mode - periodicSyncs = new ArrayList<Pair<Bundle, Long>>(); - periodicSyncs.add(Pair.create(new Bundle(), DEFAULT_POLL_FREQUENCY_SECONDS)); - } - } - - public static class SyncHistoryItem { - int authorityId; - int historyId; - long eventTime; - long elapsedTime; - int source; - int event; - long upstreamActivity; - long downstreamActivity; - String mesg; - boolean initialization; - Bundle extras; - int reason; - } - - public static class DayStats { - public final int day; - public int successCount; - public long successTime; - public int failureCount; - public long failureTime; - - public DayStats(int day) { - this.day = day; - } - } - - interface OnSyncRequestListener { - /** - * Called when a sync is needed on an account(s) due to some change in state. - * @param account - * @param userId - * @param reason - * @param authority - * @param extras - */ - public void onSyncRequest(Account account, int userId, int reason, String authority, - Bundle extras); - } - - // Primary list of all syncable authorities. Also our global lock. - private final SparseArray<AuthorityInfo> mAuthorities = - new SparseArray<AuthorityInfo>(); - - private final HashMap<AccountAndUser, AccountInfo> mAccounts - = new HashMap<AccountAndUser, AccountInfo>(); - - private final ArrayList<PendingOperation> mPendingOperations = - new ArrayList<PendingOperation>(); - - private final SparseArray<ArrayList<SyncInfo>> mCurrentSyncs - = new SparseArray<ArrayList<SyncInfo>>(); - - private final SparseArray<SyncStatusInfo> mSyncStatus = - new SparseArray<SyncStatusInfo>(); - - private final ArrayList<SyncHistoryItem> mSyncHistory = - new ArrayList<SyncHistoryItem>(); - - private final RemoteCallbackList<ISyncStatusObserver> mChangeListeners - = new RemoteCallbackList<ISyncStatusObserver>(); - - private int mNextAuthorityId = 0; - - // We keep 4 weeks of stats. - private final DayStats[] mDayStats = new DayStats[7*4]; - private final Calendar mCal; - private int mYear; - private int mYearInDays; - - private final Context mContext; - - private static volatile SyncStorageEngine sSyncStorageEngine = null; - - private int mSyncRandomOffset; - - /** - * This file contains the core engine state: all accounts and the - * settings for them. It must never be lost, and should be changed - * infrequently, so it is stored as an XML file. - */ - private final AtomicFile mAccountInfoFile; - - /** - * This file contains the current sync status. We would like to retain - * it across boots, but its loss is not the end of the world, so we store - * this information as binary data. - */ - private final AtomicFile mStatusFile; - - /** - * This file contains sync statistics. This is purely debugging information - * so is written infrequently and can be thrown away at any time. - */ - private final AtomicFile mStatisticsFile; - - /** - * This file contains the pending sync operations. It is a binary file, - * which must be updated every time an operation is added or removed, - * so we have special handling of it. - */ - private final AtomicFile mPendingFile; - private static final int PENDING_FINISH_TO_WRITE = 4; - private int mNumPendingFinished = 0; - - private int mNextHistoryId = 0; - private SparseArray<Boolean> mMasterSyncAutomatically = new SparseArray<Boolean>(); - private boolean mDefaultMasterSyncAutomatically; - - private OnSyncRequestListener mSyncRequestListener; - - private SyncStorageEngine(Context context, File dataDir) { - mContext = context; - sSyncStorageEngine = this; - - mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0")); - - mDefaultMasterSyncAutomatically = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_syncstorageengine_masterSyncAutomatically); - - File systemDir = new File(dataDir, "system"); - File syncDir = new File(systemDir, "sync"); - syncDir.mkdirs(); - mAccountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); - mStatusFile = new AtomicFile(new File(syncDir, "status.bin")); - mPendingFile = new AtomicFile(new File(syncDir, "pending.bin")); - mStatisticsFile = new AtomicFile(new File(syncDir, "stats.bin")); - - readAccountInfoLocked(); - readStatusLocked(); - readPendingOperationsLocked(); - readStatisticsLocked(); - readAndDeleteLegacyAccountInfoLocked(); - writeAccountInfoLocked(); - writeStatusLocked(); - writePendingOperationsLocked(); - writeStatisticsLocked(); - } - - public static SyncStorageEngine newTestInstance(Context context) { - return new SyncStorageEngine(context, context.getFilesDir()); - } - - public static void init(Context context) { - if (sSyncStorageEngine != null) { - return; - } - // This call will return the correct directory whether Encrypted File Systems is - // enabled or not. - File dataDir = Environment.getSecureDataDirectory(); - sSyncStorageEngine = new SyncStorageEngine(context, dataDir); - } - - public static SyncStorageEngine getSingleton() { - if (sSyncStorageEngine == null) { - throw new IllegalStateException("not initialized"); - } - return sSyncStorageEngine; - } - - protected void setOnSyncRequestListener(OnSyncRequestListener listener) { - if (mSyncRequestListener == null) { - mSyncRequestListener = listener; - } - } - - @Override public void handleMessage(Message msg) { - if (msg.what == MSG_WRITE_STATUS) { - synchronized (mAuthorities) { - writeStatusLocked(); - } - } else if (msg.what == MSG_WRITE_STATISTICS) { - synchronized (mAuthorities) { - writeStatisticsLocked(); - } - } - } - - public int getSyncRandomOffset() { - return mSyncRandomOffset; - } - - public void addStatusChangeListener(int mask, ISyncStatusObserver callback) { - synchronized (mAuthorities) { - mChangeListeners.register(callback, mask); - } - } - - public void removeStatusChangeListener(ISyncStatusObserver callback) { - synchronized (mAuthorities) { - mChangeListeners.unregister(callback); - } - } - - private void reportChange(int which) { - ArrayList<ISyncStatusObserver> reports = null; - synchronized (mAuthorities) { - int i = mChangeListeners.beginBroadcast(); - while (i > 0) { - i--; - Integer mask = (Integer)mChangeListeners.getBroadcastCookie(i); - if ((which & mask.intValue()) == 0) { - continue; - } - if (reports == null) { - reports = new ArrayList<ISyncStatusObserver>(i); - } - reports.add(mChangeListeners.getBroadcastItem(i)); - } - mChangeListeners.finishBroadcast(); - } - - if (DEBUG) { - Log.v(TAG, "reportChange " + which + " to: " + reports); - } - - if (reports != null) { - int i = reports.size(); - while (i > 0) { - i--; - try { - reports.get(i).onStatusChanged(which); - } catch (RemoteException e) { - // The remote callback list will take care of this for us. - } - } - } - } - - public boolean getSyncAutomatically(Account account, int userId, String providerName) { - synchronized (mAuthorities) { - if (account != null) { - AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, - "getSyncAutomatically"); - return authority != null && authority.enabled; - } - - int i = mAuthorities.size(); - while (i > 0) { - i--; - AuthorityInfo authority = mAuthorities.valueAt(i); - if (authority.authority.equals(providerName) - && authority.userId == userId - && authority.enabled) { - return true; - } - } - return false; - } - } - - public void setSyncAutomatically(Account account, int userId, String providerName, - boolean sync) { - if (DEBUG) { - Log.d(TAG, "setSyncAutomatically: " + /* account + */" provider " + providerName - + ", user " + userId + " -> " + sync); - } - synchronized (mAuthorities) { - AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1, - false); - if (authority.enabled == sync) { - if (DEBUG) { - Log.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing"); - } - return; - } - authority.enabled = sync; - writeAccountInfoLocked(); - } - - if (sync) { - requestSync(account, userId, SyncOperation.REASON_SYNC_AUTO, providerName, - new Bundle()); - } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); - } - - public int getIsSyncable(Account account, int userId, String providerName) { - synchronized (mAuthorities) { - if (account != null) { - AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, - "getIsSyncable"); - if (authority == null) { - return -1; - } - return authority.syncable; - } - - int i = mAuthorities.size(); - while (i > 0) { - i--; - AuthorityInfo authority = mAuthorities.valueAt(i); - if (authority.authority.equals(providerName)) { - return authority.syncable; - } - } - return -1; - } - } - - public void setIsSyncable(Account account, int userId, String providerName, int syncable) { - if (syncable > 1) { - syncable = 1; - } else if (syncable < -1) { - syncable = -1; - } - if (DEBUG) { - Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName - + ", user " + userId + " -> " + syncable); - } - synchronized (mAuthorities) { - AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1, - false); - if (authority.syncable == syncable) { - if (DEBUG) { - Log.d(TAG, "setIsSyncable: already set to " + syncable + ", doing nothing"); - } - return; - } - authority.syncable = syncable; - writeAccountInfoLocked(); - } - - if (syncable > 0) { - requestSync(account, userId, SyncOperation.REASON_IS_SYNCABLE, providerName, - new Bundle()); - } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); - } - - public Pair<Long, Long> getBackoff(Account account, int userId, String providerName) { - synchronized (mAuthorities) { - AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, - "getBackoff"); - if (authority == null || authority.backoffTime < 0) { - return null; - } - return Pair.create(authority.backoffTime, authority.backoffDelay); - } - } - - public void setBackoff(Account account, int userId, String providerName, - long nextSyncTime, long nextDelay) { - if (DEBUG) { - Log.v(TAG, "setBackoff: " + account + ", provider " + providerName - + ", user " + userId - + " -> nextSyncTime " + nextSyncTime + ", nextDelay " + nextDelay); - } - boolean changed = false; - synchronized (mAuthorities) { - if (account == null || providerName == null) { - for (AccountInfo accountInfo : mAccounts.values()) { - if (account != null && !account.equals(accountInfo.accountAndUser.account) - && userId != accountInfo.accountAndUser.userId) { - continue; - } - for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) { - if (providerName != null && !providerName.equals(authorityInfo.authority)) { - continue; - } - if (authorityInfo.backoffTime != nextSyncTime - || authorityInfo.backoffDelay != nextDelay) { - authorityInfo.backoffTime = nextSyncTime; - authorityInfo.backoffDelay = nextDelay; - changed = true; - } - } - } - } else { - AuthorityInfo authority = - getOrCreateAuthorityLocked(account, userId, providerName, -1 /* ident */, - true); - if (authority.backoffTime == nextSyncTime && authority.backoffDelay == nextDelay) { - return; - } - authority.backoffTime = nextSyncTime; - authority.backoffDelay = nextDelay; - changed = true; - } - } - - if (changed) { - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); - } - } - - public void clearAllBackoffs(SyncQueue syncQueue) { - boolean changed = false; - synchronized (mAuthorities) { - synchronized (syncQueue) { - for (AccountInfo accountInfo : mAccounts.values()) { - for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) { - if (authorityInfo.backoffTime != NOT_IN_BACKOFF_MODE - || authorityInfo.backoffDelay != NOT_IN_BACKOFF_MODE) { - if (DEBUG) { - Log.v(TAG, "clearAllBackoffs:" - + " authority:" + authorityInfo.authority - + " account:" + accountInfo.accountAndUser.account.name - + " user:" + accountInfo.accountAndUser.userId - + " backoffTime was: " + authorityInfo.backoffTime - + " backoffDelay was: " + authorityInfo.backoffDelay); - } - authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE; - authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE; - syncQueue.onBackoffChanged(accountInfo.accountAndUser.account, - accountInfo.accountAndUser.userId, authorityInfo.authority, 0); - changed = true; - } - } - } - } - } - - if (changed) { - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); - } - } - - public void setDelayUntilTime(Account account, int userId, String providerName, - long delayUntil) { - if (DEBUG) { - Log.v(TAG, "setDelayUntil: " + account + ", provider " + providerName - + ", user " + userId + " -> delayUntil " + delayUntil); - } - synchronized (mAuthorities) { - AuthorityInfo authority = getOrCreateAuthorityLocked( - account, userId, providerName, -1 /* ident */, true); - if (authority.delayUntil == delayUntil) { - return; - } - authority.delayUntil = delayUntil; - } - - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); - } - - public long getDelayUntilTime(Account account, int userId, String providerName) { - synchronized (mAuthorities) { - AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, - "getDelayUntil"); - if (authority == null) { - return 0; - } - return authority.delayUntil; - } - } - - private void updateOrRemovePeriodicSync(Account account, int userId, String providerName, - Bundle extras, - long period, boolean add) { - if (period <= 0) { - period = 0; - } - if (extras == null) { - extras = new Bundle(); - } - if (DEBUG) { - Log.v(TAG, "addOrRemovePeriodicSync: " + account + ", user " + userId - + ", provider " + providerName - + " -> period " + period + ", extras " + extras); - } - synchronized (mAuthorities) { - try { - AuthorityInfo authority = - getOrCreateAuthorityLocked(account, userId, providerName, -1, false); - if (add) { - // add this periodic sync if one with the same extras doesn't already - // exist in the periodicSyncs array - boolean alreadyPresent = false; - for (int i = 0, N = authority.periodicSyncs.size(); i < N; i++) { - Pair<Bundle, Long> syncInfo = authority.periodicSyncs.get(i); - final Bundle existingExtras = syncInfo.first; - if (equals(existingExtras, extras)) { - if (syncInfo.second == period) { - return; - } - authority.periodicSyncs.set(i, Pair.create(extras, period)); - alreadyPresent = true; - break; - } - } - // if we added an entry to the periodicSyncs array also add an entry to - // the periodic syncs status to correspond to it - if (!alreadyPresent) { - authority.periodicSyncs.add(Pair.create(extras, period)); - SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident); - status.setPeriodicSyncTime(authority.periodicSyncs.size() - 1, 0); - } - } else { - // remove any periodic syncs that match the authority and extras - SyncStatusInfo status = mSyncStatus.get(authority.ident); - boolean changed = false; - Iterator<Pair<Bundle, Long>> iterator = authority.periodicSyncs.iterator(); - int i = 0; - while (iterator.hasNext()) { - Pair<Bundle, Long> syncInfo = iterator.next(); - if (equals(syncInfo.first, extras)) { - iterator.remove(); - changed = true; - // if we removed an entry from the periodicSyncs array also - // remove the corresponding entry from the status - if (status != null) { - status.removePeriodicSyncTime(i); - } - } else { - i++; - } - } - if (!changed) { - return; - } - } - } finally { - writeAccountInfoLocked(); - writeStatusLocked(); - } - } - - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); - } - - public void addPeriodicSync(Account account, int userId, String providerName, Bundle extras, - long pollFrequency) { - updateOrRemovePeriodicSync(account, userId, providerName, extras, pollFrequency, - true /* add */); - } - - public void removePeriodicSync(Account account, int userId, String providerName, - Bundle extras) { - updateOrRemovePeriodicSync(account, userId, providerName, extras, 0 /* period, ignored */, - false /* remove */); - } - - public List<PeriodicSync> getPeriodicSyncs(Account account, int userId, String providerName) { - ArrayList<PeriodicSync> syncs = new ArrayList<PeriodicSync>(); - synchronized (mAuthorities) { - AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, - "getPeriodicSyncs"); - if (authority != null) { - for (Pair<Bundle, Long> item : authority.periodicSyncs) { - syncs.add(new PeriodicSync(account, providerName, item.first, - item.second)); - } - } - } - return syncs; - } - - public void setMasterSyncAutomatically(boolean flag, int userId) { - synchronized (mAuthorities) { - Boolean auto = mMasterSyncAutomatically.get(userId); - if (auto != null && (boolean) auto == flag) { - return; - } - mMasterSyncAutomatically.put(userId, flag); - writeAccountInfoLocked(); - } - if (flag) { - requestSync(null, userId, SyncOperation.REASON_MASTER_SYNC_AUTO, null, - new Bundle()); - } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); - mContext.sendBroadcast(SYNC_CONNECTION_SETTING_CHANGED_INTENT); - } - - public boolean getMasterSyncAutomatically(int userId) { - synchronized (mAuthorities) { - Boolean auto = mMasterSyncAutomatically.get(userId); - return auto == null ? mDefaultMasterSyncAutomatically : auto; - } - } - - public AuthorityInfo getOrCreateAuthority(Account account, int userId, String authority) { - synchronized (mAuthorities) { - return getOrCreateAuthorityLocked(account, userId, authority, - -1 /* assign a new identifier if creating a new authority */, - true /* write to storage if this results in a change */); - } - } - - public void removeAuthority(Account account, int userId, String authority) { - synchronized (mAuthorities) { - removeAuthorityLocked(account, userId, authority, true /* doWrite */); - } - } - - public AuthorityInfo getAuthority(int authorityId) { - synchronized (mAuthorities) { - return mAuthorities.get(authorityId); - } - } - - /** - * Returns true if there is currently a sync operation for the given - * account or authority actively being processed. - */ - public boolean isSyncActive(Account account, int userId, String authority) { - synchronized (mAuthorities) { - for (SyncInfo syncInfo : getCurrentSyncs(userId)) { - AuthorityInfo ainfo = getAuthority(syncInfo.authorityId); - if (ainfo != null && ainfo.account.equals(account) - && ainfo.authority.equals(authority) - && ainfo.userId == userId) { - return true; - } - } - } - - return false; - } - - public PendingOperation insertIntoPending(PendingOperation op) { - synchronized (mAuthorities) { - if (DEBUG) { - Log.v(TAG, "insertIntoPending: account=" + op.account - + " user=" + op.userId - + " auth=" + op.authority - + " src=" + op.syncSource - + " extras=" + op.extras); - } - - AuthorityInfo authority = getOrCreateAuthorityLocked(op.account, op.userId, - op.authority, - -1 /* desired identifier */, - true /* write accounts to storage */); - if (authority == null) { - return null; - } - - op = new PendingOperation(op); - op.authorityId = authority.ident; - mPendingOperations.add(op); - appendPendingOperationLocked(op); - - SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident); - status.pending = true; - } - - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING); - return op; - } - - public boolean deleteFromPending(PendingOperation op) { - boolean res = false; - synchronized (mAuthorities) { - if (DEBUG) { - Log.v(TAG, "deleteFromPending: account=" + op.account - + " user=" + op.userId - + " auth=" + op.authority - + " src=" + op.syncSource - + " extras=" + op.extras); - } - if (mPendingOperations.remove(op)) { - if (mPendingOperations.size() == 0 - || mNumPendingFinished >= PENDING_FINISH_TO_WRITE) { - writePendingOperationsLocked(); - mNumPendingFinished = 0; - } else { - mNumPendingFinished++; - } - - AuthorityInfo authority = getAuthorityLocked(op.account, op.userId, op.authority, - "deleteFromPending"); - if (authority != null) { - if (DEBUG) Log.v(TAG, "removing - " + authority); - final int N = mPendingOperations.size(); - boolean morePending = false; - for (int i=0; i<N; i++) { - PendingOperation cur = mPendingOperations.get(i); - if (cur.account.equals(op.account) - && cur.authority.equals(op.authority) - && cur.userId == op.userId) { - morePending = true; - break; - } - } - - if (!morePending) { - if (DEBUG) Log.v(TAG, "no more pending!"); - SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident); - status.pending = false; - } - } - - res = true; - } - } - - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING); - return res; - } - - /** - * Return a copy of the current array of pending operations. The - * PendingOperation objects are the real objects stored inside, so that - * they can be used with deleteFromPending(). - */ - public ArrayList<PendingOperation> getPendingOperations() { - synchronized (mAuthorities) { - return new ArrayList<PendingOperation>(mPendingOperations); - } - } - - /** - * Return the number of currently pending operations. - */ - public int getPendingOperationCount() { - synchronized (mAuthorities) { - return mPendingOperations.size(); - } - } - - /** - * Called when the set of account has changed, given the new array of - * active accounts. - */ - public void doDatabaseCleanup(Account[] accounts, int userId) { - synchronized (mAuthorities) { - if (DEBUG) Log.v(TAG, "Updating for new accounts..."); - SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>(); - Iterator<AccountInfo> accIt = mAccounts.values().iterator(); - while (accIt.hasNext()) { - AccountInfo acc = accIt.next(); - if (!ArrayUtils.contains(accounts, acc.accountAndUser.account) - && acc.accountAndUser.userId == userId) { - // This account no longer exists... - if (DEBUG) { - Log.v(TAG, "Account removed: " + acc.accountAndUser); - } - for (AuthorityInfo auth : acc.authorities.values()) { - removing.put(auth.ident, auth); - } - accIt.remove(); - } - } - - // Clean out all data structures. - int i = removing.size(); - if (i > 0) { - while (i > 0) { - i--; - int ident = removing.keyAt(i); - mAuthorities.remove(ident); - int j = mSyncStatus.size(); - while (j > 0) { - j--; - if (mSyncStatus.keyAt(j) == ident) { - mSyncStatus.remove(mSyncStatus.keyAt(j)); - } - } - j = mSyncHistory.size(); - while (j > 0) { - j--; - if (mSyncHistory.get(j).authorityId == ident) { - mSyncHistory.remove(j); - } - } - } - writeAccountInfoLocked(); - writeStatusLocked(); - writePendingOperationsLocked(); - writeStatisticsLocked(); - } - } - } - - /** - * Called when a sync is starting. Supply a valid ActiveSyncContext with information - * about the sync. - */ - public SyncInfo addActiveSync(SyncManager.ActiveSyncContext activeSyncContext) { - final SyncInfo syncInfo; - synchronized (mAuthorities) { - if (DEBUG) { - Log.v(TAG, "setActiveSync: account=" - + activeSyncContext.mSyncOperation.account - + " auth=" + activeSyncContext.mSyncOperation.authority - + " src=" + activeSyncContext.mSyncOperation.syncSource - + " extras=" + activeSyncContext.mSyncOperation.extras); - } - AuthorityInfo authority = getOrCreateAuthorityLocked( - activeSyncContext.mSyncOperation.account, - activeSyncContext.mSyncOperation.userId, - activeSyncContext.mSyncOperation.authority, - -1 /* assign a new identifier if creating a new authority */, - true /* write to storage if this results in a change */); - syncInfo = new SyncInfo(authority.ident, - authority.account, authority.authority, - activeSyncContext.mStartTime); - getCurrentSyncs(authority.userId).add(syncInfo); - } - - reportActiveChange(); - return syncInfo; - } - - /** - * Called to indicate that a previously active sync is no longer active. - */ - public void removeActiveSync(SyncInfo syncInfo, int userId) { - synchronized (mAuthorities) { - if (DEBUG) { - Log.v(TAG, "removeActiveSync: account=" + syncInfo.account - + " user=" + userId - + " auth=" + syncInfo.authority); - } - getCurrentSyncs(userId).remove(syncInfo); - } - - reportActiveChange(); - } - - /** - * To allow others to send active change reports, to poke clients. - */ - public void reportActiveChange() { - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE); - } - - /** - * Note that sync has started for the given account and authority. - */ - public long insertStartSyncEvent(Account accountName, int userId, int reason, - String authorityName, long now, int source, boolean initialization, Bundle extras) { - long id; - synchronized (mAuthorities) { - if (DEBUG) { - Log.v(TAG, "insertStartSyncEvent: account=" + accountName + "user=" + userId - + " auth=" + authorityName + " source=" + source); - } - AuthorityInfo authority = getAuthorityLocked(accountName, userId, authorityName, - "insertStartSyncEvent"); - if (authority == null) { - return -1; - } - SyncHistoryItem item = new SyncHistoryItem(); - item.initialization = initialization; - item.authorityId = authority.ident; - item.historyId = mNextHistoryId++; - if (mNextHistoryId < 0) mNextHistoryId = 0; - item.eventTime = now; - item.source = source; - item.reason = reason; - item.extras = extras; - item.event = EVENT_START; - mSyncHistory.add(0, item); - while (mSyncHistory.size() > MAX_HISTORY) { - mSyncHistory.remove(mSyncHistory.size()-1); - } - id = item.historyId; - if (DEBUG) Log.v(TAG, "returning historyId " + id); - } - - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS); - return id; - } - - public static boolean equals(Bundle b1, Bundle b2) { - if (b1.size() != b2.size()) { - return false; - } - if (b1.isEmpty()) { - return true; - } - for (String key : b1.keySet()) { - if (!b2.containsKey(key)) { - return false; - } - if (!b1.get(key).equals(b2.get(key))) { - return false; - } - } - return true; - } - - public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage, - long downstreamActivity, long upstreamActivity) { - synchronized (mAuthorities) { - if (DEBUG) { - Log.v(TAG, "stopSyncEvent: historyId=" + historyId); - } - SyncHistoryItem item = null; - int i = mSyncHistory.size(); - while (i > 0) { - i--; - item = mSyncHistory.get(i); - if (item.historyId == historyId) { - break; - } - item = null; - } - - if (item == null) { - Log.w(TAG, "stopSyncEvent: no history for id " + historyId); - return; - } - - item.elapsedTime = elapsedTime; - item.event = EVENT_STOP; - item.mesg = resultMessage; - item.downstreamActivity = downstreamActivity; - item.upstreamActivity = upstreamActivity; - - SyncStatusInfo status = getOrCreateSyncStatusLocked(item.authorityId); - - status.numSyncs++; - status.totalElapsedTime += elapsedTime; - switch (item.source) { - case SOURCE_LOCAL: - status.numSourceLocal++; - break; - case SOURCE_POLL: - status.numSourcePoll++; - break; - case SOURCE_USER: - status.numSourceUser++; - break; - case SOURCE_SERVER: - status.numSourceServer++; - break; - case SOURCE_PERIODIC: - status.numSourcePeriodic++; - break; - } - - boolean writeStatisticsNow = false; - int day = getCurrentDayLocked(); - if (mDayStats[0] == null) { - mDayStats[0] = new DayStats(day); - } else if (day != mDayStats[0].day) { - System.arraycopy(mDayStats, 0, mDayStats, 1, mDayStats.length-1); - mDayStats[0] = new DayStats(day); - writeStatisticsNow = true; - } else if (mDayStats[0] == null) { - } - final DayStats ds = mDayStats[0]; - - final long lastSyncTime = (item.eventTime + elapsedTime); - boolean writeStatusNow = false; - if (MESG_SUCCESS.equals(resultMessage)) { - // - if successful, update the successful columns - if (status.lastSuccessTime == 0 || status.lastFailureTime != 0) { - writeStatusNow = true; - } - status.lastSuccessTime = lastSyncTime; - status.lastSuccessSource = item.source; - status.lastFailureTime = 0; - status.lastFailureSource = -1; - status.lastFailureMesg = null; - status.initialFailureTime = 0; - ds.successCount++; - ds.successTime += elapsedTime; - } else if (!MESG_CANCELED.equals(resultMessage)) { - if (status.lastFailureTime == 0) { - writeStatusNow = true; - } - status.lastFailureTime = lastSyncTime; - status.lastFailureSource = item.source; - status.lastFailureMesg = resultMessage; - if (status.initialFailureTime == 0) { - status.initialFailureTime = lastSyncTime; - } - ds.failureCount++; - ds.failureTime += elapsedTime; - } - - if (writeStatusNow) { - writeStatusLocked(); - } else if (!hasMessages(MSG_WRITE_STATUS)) { - sendMessageDelayed(obtainMessage(MSG_WRITE_STATUS), - WRITE_STATUS_DELAY); - } - if (writeStatisticsNow) { - writeStatisticsLocked(); - } else if (!hasMessages(MSG_WRITE_STATISTICS)) { - sendMessageDelayed(obtainMessage(MSG_WRITE_STATISTICS), - WRITE_STATISTICS_DELAY); - } - } - - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS); - } - - /** - * Return a list of the currently active syncs. Note that the returned items are the - * real, live active sync objects, so be careful what you do with it. - */ - public List<SyncInfo> getCurrentSyncs(int userId) { - synchronized (mAuthorities) { - ArrayList<SyncInfo> syncs = mCurrentSyncs.get(userId); - if (syncs == null) { - syncs = new ArrayList<SyncInfo>(); - mCurrentSyncs.put(userId, syncs); - } - return syncs; - } - } - - /** - * Return an array of the current sync status for all authorities. Note - * that the objects inside the array are the real, live status objects, - * so be careful what you do with them. - */ - public ArrayList<SyncStatusInfo> getSyncStatus() { - synchronized (mAuthorities) { - final int N = mSyncStatus.size(); - ArrayList<SyncStatusInfo> ops = new ArrayList<SyncStatusInfo>(N); - for (int i=0; i<N; i++) { - ops.add(mSyncStatus.valueAt(i)); - } - return ops; - } - } - - /** - * Return an array of the current authorities. Note - * that the objects inside the array are the real, live objects, - * so be careful what you do with them. - */ - public ArrayList<AuthorityInfo> getAuthorities() { - synchronized (mAuthorities) { - final int N = mAuthorities.size(); - ArrayList<AuthorityInfo> infos = new ArrayList<AuthorityInfo>(N); - for (int i=0; i<N; i++) { - // Make deep copy because AuthorityInfo syncs are liable to change. - infos.add(new AuthorityInfo(mAuthorities.valueAt(i))); - } - return infos; - } - } - - /** - * Returns the status that matches the authority and account. - * - * @param account the account we want to check - * @param authority the authority whose row should be selected - * @return the SyncStatusInfo for the authority - */ - public SyncStatusInfo getStatusByAccountAndAuthority(Account account, int userId, - String authority) { - if (account == null || authority == null) { - throw new IllegalArgumentException(); - } - synchronized (mAuthorities) { - final int N = mSyncStatus.size(); - for (int i=0; i<N; i++) { - SyncStatusInfo cur = mSyncStatus.valueAt(i); - AuthorityInfo ainfo = mAuthorities.get(cur.authorityId); - - if (ainfo != null && ainfo.authority.equals(authority) - && ainfo.userId == userId - && account.equals(ainfo.account)) { - return cur; - } - } - return null; - } - } - - /** - * Return true if the pending status is true of any matching authorities. - */ - public boolean isSyncPending(Account account, int userId, String authority) { - synchronized (mAuthorities) { - final int N = mSyncStatus.size(); - for (int i=0; i<N; i++) { - SyncStatusInfo cur = mSyncStatus.valueAt(i); - AuthorityInfo ainfo = mAuthorities.get(cur.authorityId); - if (ainfo == null) { - continue; - } - if (userId != ainfo.userId) { - continue; - } - if (account != null && !ainfo.account.equals(account)) { - continue; - } - if (ainfo.authority.equals(authority) && cur.pending) { - return true; - } - } - return false; - } - } - - /** - * Return an array of the current sync status for all authorities. Note - * that the objects inside the array are the real, live status objects, - * so be careful what you do with them. - */ - public ArrayList<SyncHistoryItem> getSyncHistory() { - synchronized (mAuthorities) { - final int N = mSyncHistory.size(); - ArrayList<SyncHistoryItem> items = new ArrayList<SyncHistoryItem>(N); - for (int i=0; i<N; i++) { - items.add(mSyncHistory.get(i)); - } - return items; - } - } - - /** - * Return an array of the current per-day statistics. Note - * that the objects inside the array are the real, live status objects, - * so be careful what you do with them. - */ - public DayStats[] getDayStatistics() { - synchronized (mAuthorities) { - DayStats[] ds = new DayStats[mDayStats.length]; - System.arraycopy(mDayStats, 0, ds, 0, ds.length); - return ds; - } - } - - private int getCurrentDayLocked() { - mCal.setTimeInMillis(System.currentTimeMillis()); - final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR); - if (mYear != mCal.get(Calendar.YEAR)) { - mYear = mCal.get(Calendar.YEAR); - mCal.clear(); - mCal.set(Calendar.YEAR, mYear); - mYearInDays = (int)(mCal.getTimeInMillis()/86400000); - } - return dayOfYear + mYearInDays; - } - - /** - * Retrieve an authority, returning null if one does not exist. - * - * @param accountName The name of the account for the authority. - * @param authorityName The name of the authority itself. - * @param tag If non-null, this will be used in a log message if the - * requested authority does not exist. - */ - private AuthorityInfo getAuthorityLocked(Account accountName, int userId, String authorityName, - String tag) { - AccountAndUser au = new AccountAndUser(accountName, userId); - AccountInfo accountInfo = mAccounts.get(au); - if (accountInfo == null) { - if (tag != null) { - if (DEBUG) { - Log.v(TAG, tag + ": unknown account " + au); - } - } - return null; - } - AuthorityInfo authority = accountInfo.authorities.get(authorityName); - if (authority == null) { - if (tag != null) { - if (DEBUG) { - Log.v(TAG, tag + ": unknown authority " + authorityName); - } - } - return null; - } - - return authority; - } - - private AuthorityInfo getOrCreateAuthorityLocked(Account accountName, int userId, - String authorityName, int ident, boolean doWrite) { - AccountAndUser au = new AccountAndUser(accountName, userId); - AccountInfo account = mAccounts.get(au); - if (account == null) { - account = new AccountInfo(au); - mAccounts.put(au, account); - } - AuthorityInfo authority = account.authorities.get(authorityName); - if (authority == null) { - if (ident < 0) { - ident = mNextAuthorityId; - mNextAuthorityId++; - doWrite = true; - } - if (DEBUG) { - Log.v(TAG, "created a new AuthorityInfo for " + accountName - + ", user " + userId - + ", provider " + authorityName); - } - authority = new AuthorityInfo(accountName, userId, authorityName, ident); - account.authorities.put(authorityName, authority); - mAuthorities.put(ident, authority); - if (doWrite) { - writeAccountInfoLocked(); - } - } - - return authority; - } - - private void removeAuthorityLocked(Account account, int userId, String authorityName, - boolean doWrite) { - AccountInfo accountInfo = mAccounts.get(new AccountAndUser(account, userId)); - if (accountInfo != null) { - final AuthorityInfo authorityInfo = accountInfo.authorities.remove(authorityName); - if (authorityInfo != null) { - mAuthorities.remove(authorityInfo.ident); - if (doWrite) { - writeAccountInfoLocked(); - } - } - } - } - - public SyncStatusInfo getOrCreateSyncStatus(AuthorityInfo authority) { - synchronized (mAuthorities) { - return getOrCreateSyncStatusLocked(authority.ident); - } - } - - private SyncStatusInfo getOrCreateSyncStatusLocked(int authorityId) { - SyncStatusInfo status = mSyncStatus.get(authorityId); - if (status == null) { - status = new SyncStatusInfo(authorityId); - mSyncStatus.put(authorityId, status); - } - return status; - } - - public void writeAllState() { - synchronized (mAuthorities) { - // Account info is always written so no need to do it here. - - if (mNumPendingFinished > 0) { - // Only write these if they are out of date. - writePendingOperationsLocked(); - } - - // Just always write these... they are likely out of date. - writeStatusLocked(); - writeStatisticsLocked(); - } - } - - /** - * public for testing - */ - public void clearAndReadState() { - synchronized (mAuthorities) { - mAuthorities.clear(); - mAccounts.clear(); - mPendingOperations.clear(); - mSyncStatus.clear(); - mSyncHistory.clear(); - - readAccountInfoLocked(); - readStatusLocked(); - readPendingOperationsLocked(); - readStatisticsLocked(); - readAndDeleteLegacyAccountInfoLocked(); - writeAccountInfoLocked(); - writeStatusLocked(); - writePendingOperationsLocked(); - writeStatisticsLocked(); - } - } - - /** - * Read all account information back in to the initial engine state. - */ - private void readAccountInfoLocked() { - int highestAuthorityId = -1; - FileInputStream fis = null; - try { - fis = mAccountInfoFile.openRead(); - if (DEBUG_FILE) Log.v(TAG, "Reading " + mAccountInfoFile.getBaseFile()); - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(fis, null); - int eventType = parser.getEventType(); - while (eventType != XmlPullParser.START_TAG) { - eventType = parser.next(); - } - String tagName = parser.getName(); - if ("accounts".equals(tagName)) { - String listen = parser.getAttributeValue(null, XML_ATTR_LISTEN_FOR_TICKLES); - String versionString = parser.getAttributeValue(null, "version"); - int version; - try { - version = (versionString == null) ? 0 : Integer.parseInt(versionString); - } catch (NumberFormatException e) { - version = 0; - } - String nextIdString = parser.getAttributeValue(null, XML_ATTR_NEXT_AUTHORITY_ID); - try { - int id = (nextIdString == null) ? 0 : Integer.parseInt(nextIdString); - mNextAuthorityId = Math.max(mNextAuthorityId, id); - } catch (NumberFormatException e) { - // don't care - } - String offsetString = parser.getAttributeValue(null, XML_ATTR_SYNC_RANDOM_OFFSET); - try { - mSyncRandomOffset = (offsetString == null) ? 0 : Integer.parseInt(offsetString); - } catch (NumberFormatException e) { - mSyncRandomOffset = 0; - } - if (mSyncRandomOffset == 0) { - Random random = new Random(System.currentTimeMillis()); - mSyncRandomOffset = random.nextInt(86400); - } - mMasterSyncAutomatically.put(0, listen == null || Boolean.parseBoolean(listen)); - eventType = parser.next(); - AuthorityInfo authority = null; - Pair<Bundle, Long> periodicSync = null; - do { - if (eventType == XmlPullParser.START_TAG) { - tagName = parser.getName(); - if (parser.getDepth() == 2) { - if ("authority".equals(tagName)) { - authority = parseAuthority(parser, version); - periodicSync = null; - if (authority.ident > highestAuthorityId) { - highestAuthorityId = authority.ident; - } - } else if (XML_TAG_LISTEN_FOR_TICKLES.equals(tagName)) { - parseListenForTickles(parser); - } - } else if (parser.getDepth() == 3) { - if ("periodicSync".equals(tagName) && authority != null) { - periodicSync = parsePeriodicSync(parser, authority); - } - } else if (parser.getDepth() == 4 && periodicSync != null) { - if ("extra".equals(tagName)) { - parseExtra(parser, periodicSync); - } - } - } - eventType = parser.next(); - } while (eventType != XmlPullParser.END_DOCUMENT); - } - } catch (XmlPullParserException e) { - Log.w(TAG, "Error reading accounts", e); - return; - } catch (java.io.IOException e) { - if (fis == null) Log.i(TAG, "No initial accounts"); - else Log.w(TAG, "Error reading accounts", e); - return; - } finally { - mNextAuthorityId = Math.max(highestAuthorityId + 1, mNextAuthorityId); - if (fis != null) { - try { - fis.close(); - } catch (java.io.IOException e1) { - } - } - } - - maybeMigrateSettingsForRenamedAuthorities(); - } - - /** - * some authority names have changed. copy over their settings and delete the old ones - * @return true if a change was made - */ - private boolean maybeMigrateSettingsForRenamedAuthorities() { - boolean writeNeeded = false; - - ArrayList<AuthorityInfo> authoritiesToRemove = new ArrayList<AuthorityInfo>(); - final int N = mAuthorities.size(); - for (int i=0; i<N; i++) { - AuthorityInfo authority = mAuthorities.valueAt(i); - // skip this authority if it isn't one of the renamed ones - final String newAuthorityName = sAuthorityRenames.get(authority.authority); - if (newAuthorityName == null) { - continue; - } - - // remember this authority so we can remove it later. we can't remove it - // now without messing up this loop iteration - authoritiesToRemove.add(authority); - - // this authority isn't enabled, no need to copy it to the new authority name since - // the default is "disabled" - if (!authority.enabled) { - continue; - } - - // if we already have a record of this new authority then don't copy over the settings - if (getAuthorityLocked(authority.account, authority.userId, newAuthorityName, "cleanup") - != null) { - continue; - } - - AuthorityInfo newAuthority = getOrCreateAuthorityLocked(authority.account, - authority.userId, newAuthorityName, -1 /* ident */, false /* doWrite */); - newAuthority.enabled = true; - writeNeeded = true; - } - - for (AuthorityInfo authorityInfo : authoritiesToRemove) { - removeAuthorityLocked(authorityInfo.account, authorityInfo.userId, - authorityInfo.authority, false /* doWrite */); - writeNeeded = true; - } - - return writeNeeded; - } - - private void parseListenForTickles(XmlPullParser parser) { - String user = parser.getAttributeValue(null, XML_ATTR_USER); - int userId = 0; - try { - userId = Integer.parseInt(user); - } catch (NumberFormatException e) { - Log.e(TAG, "error parsing the user for listen-for-tickles", e); - } catch (NullPointerException e) { - Log.e(TAG, "the user in listen-for-tickles is null", e); - } - String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED); - boolean listen = enabled == null || Boolean.parseBoolean(enabled); - mMasterSyncAutomatically.put(userId, listen); - } - - private AuthorityInfo parseAuthority(XmlPullParser parser, int version) { - AuthorityInfo authority = null; - int id = -1; - try { - id = Integer.parseInt(parser.getAttributeValue( - null, "id")); - } catch (NumberFormatException e) { - Log.e(TAG, "error parsing the id of the authority", e); - } catch (NullPointerException e) { - Log.e(TAG, "the id of the authority is null", e); - } - if (id >= 0) { - String authorityName = parser.getAttributeValue(null, "authority"); - String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED); - String syncable = parser.getAttributeValue(null, "syncable"); - String accountName = parser.getAttributeValue(null, "account"); - String accountType = parser.getAttributeValue(null, "type"); - String user = parser.getAttributeValue(null, XML_ATTR_USER); - int userId = user == null ? 0 : Integer.parseInt(user); - if (accountType == null) { - accountType = "com.google"; - syncable = "unknown"; - } - authority = mAuthorities.get(id); - if (DEBUG_FILE) Log.v(TAG, "Adding authority: account=" - + accountName + " auth=" + authorityName - + " user=" + userId - + " enabled=" + enabled - + " syncable=" + syncable); - if (authority == null) { - if (DEBUG_FILE) Log.v(TAG, "Creating entry"); - authority = getOrCreateAuthorityLocked( - new Account(accountName, accountType), userId, authorityName, id, false); - // If the version is 0 then we are upgrading from a file format that did not - // know about periodic syncs. In that case don't clear the list since we - // want the default, which is a daily periodioc sync. - // Otherwise clear out this default list since we will populate it later with - // the periodic sync descriptions that are read from the configuration file. - if (version > 0) { - authority.periodicSyncs.clear(); - } - } - if (authority != null) { - authority.enabled = enabled == null || Boolean.parseBoolean(enabled); - if ("unknown".equals(syncable)) { - authority.syncable = -1; - } else { - authority.syncable = - (syncable == null || Boolean.parseBoolean(syncable)) ? 1 : 0; - } - } else { - Log.w(TAG, "Failure adding authority: account=" - + accountName + " auth=" + authorityName - + " enabled=" + enabled - + " syncable=" + syncable); - } - } - - return authority; - } - - private Pair<Bundle, Long> parsePeriodicSync(XmlPullParser parser, AuthorityInfo authority) { - Bundle extras = new Bundle(); - String periodValue = parser.getAttributeValue(null, "period"); - final long period; - try { - period = Long.parseLong(periodValue); - } catch (NumberFormatException e) { - Log.e(TAG, "error parsing the period of a periodic sync", e); - return null; - } catch (NullPointerException e) { - Log.e(TAG, "the period of a periodic sync is null", e); - return null; - } - final Pair<Bundle, Long> periodicSync = Pair.create(extras, period); - authority.periodicSyncs.add(periodicSync); - - return periodicSync; - } - - private void parseExtra(XmlPullParser parser, Pair<Bundle, Long> periodicSync) { - final Bundle extras = periodicSync.first; - String name = parser.getAttributeValue(null, "name"); - String type = parser.getAttributeValue(null, "type"); - String value1 = parser.getAttributeValue(null, "value1"); - String value2 = parser.getAttributeValue(null, "value2"); - - try { - if ("long".equals(type)) { - extras.putLong(name, Long.parseLong(value1)); - } else if ("integer".equals(type)) { - extras.putInt(name, Integer.parseInt(value1)); - } else if ("double".equals(type)) { - extras.putDouble(name, Double.parseDouble(value1)); - } else if ("float".equals(type)) { - extras.putFloat(name, Float.parseFloat(value1)); - } else if ("boolean".equals(type)) { - extras.putBoolean(name, Boolean.parseBoolean(value1)); - } else if ("string".equals(type)) { - extras.putString(name, value1); - } else if ("account".equals(type)) { - extras.putParcelable(name, new Account(value1, value2)); - } - } catch (NumberFormatException e) { - Log.e(TAG, "error parsing bundle value", e); - } catch (NullPointerException e) { - Log.e(TAG, "error parsing bundle value", e); - } - } - - /** - * Write all account information to the account file. - */ - private void writeAccountInfoLocked() { - if (DEBUG_FILE) Log.v(TAG, "Writing new " + mAccountInfoFile.getBaseFile()); - FileOutputStream fos = null; - - try { - fos = mAccountInfoFile.startWrite(); - XmlSerializer out = new FastXmlSerializer(); - out.setOutput(fos, "utf-8"); - out.startDocument(null, true); - out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - - out.startTag(null, "accounts"); - out.attribute(null, "version", Integer.toString(ACCOUNTS_VERSION)); - out.attribute(null, XML_ATTR_NEXT_AUTHORITY_ID, Integer.toString(mNextAuthorityId)); - out.attribute(null, XML_ATTR_SYNC_RANDOM_OFFSET, Integer.toString(mSyncRandomOffset)); - - // Write the Sync Automatically flags for each user - final int M = mMasterSyncAutomatically.size(); - for (int m = 0; m < M; m++) { - int userId = mMasterSyncAutomatically.keyAt(m); - Boolean listen = mMasterSyncAutomatically.valueAt(m); - out.startTag(null, XML_TAG_LISTEN_FOR_TICKLES); - out.attribute(null, XML_ATTR_USER, Integer.toString(userId)); - out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(listen)); - out.endTag(null, XML_TAG_LISTEN_FOR_TICKLES); - } - - final int N = mAuthorities.size(); - for (int i=0; i<N; i++) { - AuthorityInfo authority = mAuthorities.valueAt(i); - out.startTag(null, "authority"); - out.attribute(null, "id", Integer.toString(authority.ident)); - out.attribute(null, "account", authority.account.name); - out.attribute(null, XML_ATTR_USER, Integer.toString(authority.userId)); - out.attribute(null, "type", authority.account.type); - out.attribute(null, "authority", authority.authority); - out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(authority.enabled)); - if (authority.syncable < 0) { - out.attribute(null, "syncable", "unknown"); - } else { - out.attribute(null, "syncable", Boolean.toString(authority.syncable != 0)); - } - for (Pair<Bundle, Long> periodicSync : authority.periodicSyncs) { - out.startTag(null, "periodicSync"); - out.attribute(null, "period", Long.toString(periodicSync.second)); - final Bundle extras = periodicSync.first; - for (String key : extras.keySet()) { - out.startTag(null, "extra"); - out.attribute(null, "name", key); - final Object value = extras.get(key); - if (value instanceof Long) { - out.attribute(null, "type", "long"); - out.attribute(null, "value1", value.toString()); - } else if (value instanceof Integer) { - out.attribute(null, "type", "integer"); - out.attribute(null, "value1", value.toString()); - } else if (value instanceof Boolean) { - out.attribute(null, "type", "boolean"); - out.attribute(null, "value1", value.toString()); - } else if (value instanceof Float) { - out.attribute(null, "type", "float"); - out.attribute(null, "value1", value.toString()); - } else if (value instanceof Double) { - out.attribute(null, "type", "double"); - out.attribute(null, "value1", value.toString()); - } else if (value instanceof String) { - out.attribute(null, "type", "string"); - out.attribute(null, "value1", value.toString()); - } else if (value instanceof Account) { - out.attribute(null, "type", "account"); - out.attribute(null, "value1", ((Account)value).name); - out.attribute(null, "value2", ((Account)value).type); - } - out.endTag(null, "extra"); - } - out.endTag(null, "periodicSync"); - } - out.endTag(null, "authority"); - } - - out.endTag(null, "accounts"); - - out.endDocument(); - - mAccountInfoFile.finishWrite(fos); - } catch (java.io.IOException e1) { - Log.w(TAG, "Error writing accounts", e1); - if (fos != null) { - mAccountInfoFile.failWrite(fos); - } - } - } - - static int getIntColumn(Cursor c, String name) { - return c.getInt(c.getColumnIndex(name)); - } - - static long getLongColumn(Cursor c, String name) { - return c.getLong(c.getColumnIndex(name)); - } - - /** - * Load sync engine state from the old syncmanager database, and then - * erase it. Note that we don't deal with pending operations, active - * sync, or history. - */ - private void readAndDeleteLegacyAccountInfoLocked() { - // Look for old database to initialize from. - File file = mContext.getDatabasePath("syncmanager.db"); - if (!file.exists()) { - return; - } - String path = file.getPath(); - SQLiteDatabase db = null; - try { - db = SQLiteDatabase.openDatabase(path, null, - SQLiteDatabase.OPEN_READONLY); - } catch (SQLiteException e) { - } - - if (db != null) { - final boolean hasType = db.getVersion() >= 11; - - // Copy in all of the status information, as well as accounts. - if (DEBUG_FILE) Log.v(TAG, "Reading legacy sync accounts db"); - SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - qb.setTables("stats, status"); - HashMap<String,String> map = new HashMap<String,String>(); - map.put("_id", "status._id as _id"); - map.put("account", "stats.account as account"); - if (hasType) { - map.put("account_type", "stats.account_type as account_type"); - } - map.put("authority", "stats.authority as authority"); - map.put("totalElapsedTime", "totalElapsedTime"); - map.put("numSyncs", "numSyncs"); - map.put("numSourceLocal", "numSourceLocal"); - map.put("numSourcePoll", "numSourcePoll"); - map.put("numSourceServer", "numSourceServer"); - map.put("numSourceUser", "numSourceUser"); - map.put("lastSuccessSource", "lastSuccessSource"); - map.put("lastSuccessTime", "lastSuccessTime"); - map.put("lastFailureSource", "lastFailureSource"); - map.put("lastFailureTime", "lastFailureTime"); - map.put("lastFailureMesg", "lastFailureMesg"); - map.put("pending", "pending"); - qb.setProjectionMap(map); - qb.appendWhere("stats._id = status.stats_id"); - Cursor c = qb.query(db, null, null, null, null, null, null); - while (c.moveToNext()) { - String accountName = c.getString(c.getColumnIndex("account")); - String accountType = hasType - ? c.getString(c.getColumnIndex("account_type")) : null; - if (accountType == null) { - accountType = "com.google"; - } - String authorityName = c.getString(c.getColumnIndex("authority")); - AuthorityInfo authority = this.getOrCreateAuthorityLocked( - new Account(accountName, accountType), 0 /* legacy is single-user */, - authorityName, -1, false); - if (authority != null) { - int i = mSyncStatus.size(); - boolean found = false; - SyncStatusInfo st = null; - while (i > 0) { - i--; - st = mSyncStatus.valueAt(i); - if (st.authorityId == authority.ident) { - found = true; - break; - } - } - if (!found) { - st = new SyncStatusInfo(authority.ident); - mSyncStatus.put(authority.ident, st); - } - st.totalElapsedTime = getLongColumn(c, "totalElapsedTime"); - st.numSyncs = getIntColumn(c, "numSyncs"); - st.numSourceLocal = getIntColumn(c, "numSourceLocal"); - st.numSourcePoll = getIntColumn(c, "numSourcePoll"); - st.numSourceServer = getIntColumn(c, "numSourceServer"); - st.numSourceUser = getIntColumn(c, "numSourceUser"); - st.numSourcePeriodic = 0; - st.lastSuccessSource = getIntColumn(c, "lastSuccessSource"); - st.lastSuccessTime = getLongColumn(c, "lastSuccessTime"); - st.lastFailureSource = getIntColumn(c, "lastFailureSource"); - st.lastFailureTime = getLongColumn(c, "lastFailureTime"); - st.lastFailureMesg = c.getString(c.getColumnIndex("lastFailureMesg")); - st.pending = getIntColumn(c, "pending") != 0; - } - } - - c.close(); - - // Retrieve the settings. - qb = new SQLiteQueryBuilder(); - qb.setTables("settings"); - c = qb.query(db, null, null, null, null, null, null); - while (c.moveToNext()) { - String name = c.getString(c.getColumnIndex("name")); - String value = c.getString(c.getColumnIndex("value")); - if (name == null) continue; - if (name.equals("listen_for_tickles")) { - setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value), 0); - } else if (name.startsWith("sync_provider_")) { - String provider = name.substring("sync_provider_".length(), - name.length()); - int i = mAuthorities.size(); - while (i > 0) { - i--; - AuthorityInfo authority = mAuthorities.valueAt(i); - if (authority.authority.equals(provider)) { - authority.enabled = value == null || Boolean.parseBoolean(value); - authority.syncable = 1; - } - } - } - } - - c.close(); - - db.close(); - - (new File(path)).delete(); - } - } - - public static final int STATUS_FILE_END = 0; - public static final int STATUS_FILE_ITEM = 100; - - /** - * Read all sync status back in to the initial engine state. - */ - private void readStatusLocked() { - if (DEBUG_FILE) Log.v(TAG, "Reading " + mStatusFile.getBaseFile()); - try { - byte[] data = mStatusFile.readFully(); - Parcel in = Parcel.obtain(); - in.unmarshall(data, 0, data.length); - in.setDataPosition(0); - int token; - while ((token=in.readInt()) != STATUS_FILE_END) { - if (token == STATUS_FILE_ITEM) { - SyncStatusInfo status = new SyncStatusInfo(in); - if (mAuthorities.indexOfKey(status.authorityId) >= 0) { - status.pending = false; - if (DEBUG_FILE) Log.v(TAG, "Adding status for id " - + status.authorityId); - mSyncStatus.put(status.authorityId, status); - } - } else { - // Ooops. - Log.w(TAG, "Unknown status token: " + token); - break; - } - } - } catch (java.io.IOException e) { - Log.i(TAG, "No initial status"); - } - } - - /** - * Write all sync status to the sync status file. - */ - private void writeStatusLocked() { - if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatusFile.getBaseFile()); - - // The file is being written, so we don't need to have a scheduled - // write until the next change. - removeMessages(MSG_WRITE_STATUS); - - FileOutputStream fos = null; - try { - fos = mStatusFile.startWrite(); - Parcel out = Parcel.obtain(); - final int N = mSyncStatus.size(); - for (int i=0; i<N; i++) { - SyncStatusInfo status = mSyncStatus.valueAt(i); - out.writeInt(STATUS_FILE_ITEM); - status.writeToParcel(out, 0); - } - out.writeInt(STATUS_FILE_END); - fos.write(out.marshall()); - out.recycle(); - - mStatusFile.finishWrite(fos); - } catch (java.io.IOException e1) { - Log.w(TAG, "Error writing status", e1); - if (fos != null) { - mStatusFile.failWrite(fos); - } - } - } - - public static final int PENDING_OPERATION_VERSION = 3; - - /** - * Read all pending operations back in to the initial engine state. - */ - private void readPendingOperationsLocked() { - if (DEBUG_FILE) Log.v(TAG, "Reading " + mPendingFile.getBaseFile()); - try { - byte[] data = mPendingFile.readFully(); - Parcel in = Parcel.obtain(); - in.unmarshall(data, 0, data.length); - in.setDataPosition(0); - final int SIZE = in.dataSize(); - while (in.dataPosition() < SIZE) { - int version = in.readInt(); - if (version != PENDING_OPERATION_VERSION && version != 1) { - Log.w(TAG, "Unknown pending operation version " - + version + "; dropping all ops"); - break; - } - int authorityId = in.readInt(); - int syncSource = in.readInt(); - byte[] flatExtras = in.createByteArray(); - boolean expedited; - if (version == PENDING_OPERATION_VERSION) { - expedited = in.readInt() != 0; - } else { - expedited = false; - } - int reason = in.readInt(); - AuthorityInfo authority = mAuthorities.get(authorityId); - if (authority != null) { - Bundle extras; - if (flatExtras != null) { - extras = unflattenBundle(flatExtras); - } else { - // if we are unable to parse the extras for whatever reason convert this - // to a regular sync by creating an empty extras - extras = new Bundle(); - } - PendingOperation op = new PendingOperation( - authority.account, authority.userId, reason, syncSource, - authority.authority, extras, expedited); - op.authorityId = authorityId; - op.flatExtras = flatExtras; - if (DEBUG_FILE) Log.v(TAG, "Adding pending op: account=" + op.account - + " auth=" + op.authority - + " src=" + op.syncSource - + " reason=" + op.reason - + " expedited=" + op.expedited - + " extras=" + op.extras); - mPendingOperations.add(op); - } - } - } catch (java.io.IOException e) { - Log.i(TAG, "No initial pending operations"); - } - } - - private void writePendingOperationLocked(PendingOperation op, Parcel out) { - out.writeInt(PENDING_OPERATION_VERSION); - out.writeInt(op.authorityId); - out.writeInt(op.syncSource); - if (op.flatExtras == null && op.extras != null) { - op.flatExtras = flattenBundle(op.extras); - } - out.writeByteArray(op.flatExtras); - out.writeInt(op.expedited ? 1 : 0); - out.writeInt(op.reason); - } - - /** - * Write all currently pending ops to the pending ops file. - */ - private void writePendingOperationsLocked() { - final int N = mPendingOperations.size(); - FileOutputStream fos = null; - try { - if (N == 0) { - if (DEBUG_FILE) Log.v(TAG, "Truncating " + mPendingFile.getBaseFile()); - mPendingFile.truncate(); - return; - } - - if (DEBUG_FILE) Log.v(TAG, "Writing new " + mPendingFile.getBaseFile()); - fos = mPendingFile.startWrite(); - - Parcel out = Parcel.obtain(); - for (int i=0; i<N; i++) { - PendingOperation op = mPendingOperations.get(i); - writePendingOperationLocked(op, out); - } - fos.write(out.marshall()); - out.recycle(); - - mPendingFile.finishWrite(fos); - } catch (java.io.IOException e1) { - Log.w(TAG, "Error writing pending operations", e1); - if (fos != null) { - mPendingFile.failWrite(fos); - } - } - } - - /** - * Append the given operation to the pending ops file; if unable to, - * write all pending ops. - */ - private void appendPendingOperationLocked(PendingOperation op) { - if (DEBUG_FILE) Log.v(TAG, "Appending to " + mPendingFile.getBaseFile()); - FileOutputStream fos = null; - try { - fos = mPendingFile.openAppend(); - } catch (java.io.IOException e) { - if (DEBUG_FILE) Log.v(TAG, "Failed append; writing full file"); - writePendingOperationsLocked(); - return; - } - - try { - Parcel out = Parcel.obtain(); - writePendingOperationLocked(op, out); - fos.write(out.marshall()); - out.recycle(); - } catch (java.io.IOException e1) { - Log.w(TAG, "Error writing pending operations", e1); - } finally { - try { - fos.close(); - } catch (java.io.IOException e2) { - } - } - } - - static private byte[] flattenBundle(Bundle bundle) { - byte[] flatData = null; - Parcel parcel = Parcel.obtain(); - try { - bundle.writeToParcel(parcel, 0); - flatData = parcel.marshall(); - } finally { - parcel.recycle(); - } - return flatData; - } - - static private Bundle unflattenBundle(byte[] flatData) { - Bundle bundle; - Parcel parcel = Parcel.obtain(); - try { - parcel.unmarshall(flatData, 0, flatData.length); - parcel.setDataPosition(0); - bundle = parcel.readBundle(); - } catch (RuntimeException e) { - // A RuntimeException is thrown if we were unable to parse the parcel. - // Create an empty parcel in this case. - bundle = new Bundle(); - } finally { - parcel.recycle(); - } - return bundle; - } - - private void requestSync(Account account, int userId, int reason, String authority, - Bundle extras) { - // If this is happening in the system process, then call the syncrequest listener - // to make a request back to the SyncManager directly. - // If this is probably a test instance, then call back through the ContentResolver - // which will know which userId to apply based on the Binder id. - if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID - && mSyncRequestListener != null) { - mSyncRequestListener.onSyncRequest(account, userId, reason, authority, extras); - } else { - ContentResolver.requestSync(account, authority, extras); - } - } - - public static final int STATISTICS_FILE_END = 0; - public static final int STATISTICS_FILE_ITEM_OLD = 100; - public static final int STATISTICS_FILE_ITEM = 101; - - /** - * Read all sync statistics back in to the initial engine state. - */ - private void readStatisticsLocked() { - try { - byte[] data = mStatisticsFile.readFully(); - Parcel in = Parcel.obtain(); - in.unmarshall(data, 0, data.length); - in.setDataPosition(0); - int token; - int index = 0; - while ((token=in.readInt()) != STATISTICS_FILE_END) { - if (token == STATISTICS_FILE_ITEM - || token == STATISTICS_FILE_ITEM_OLD) { - int day = in.readInt(); - if (token == STATISTICS_FILE_ITEM_OLD) { - day = day - 2009 + 14245; // Magic! - } - DayStats ds = new DayStats(day); - ds.successCount = in.readInt(); - ds.successTime = in.readLong(); - ds.failureCount = in.readInt(); - ds.failureTime = in.readLong(); - if (index < mDayStats.length) { - mDayStats[index] = ds; - index++; - } - } else { - // Ooops. - Log.w(TAG, "Unknown stats token: " + token); - break; - } - } - } catch (java.io.IOException e) { - Log.i(TAG, "No initial statistics"); - } - } - - /** - * Write all sync statistics to the sync status file. - */ - private void writeStatisticsLocked() { - if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatisticsFile.getBaseFile()); - - // The file is being written, so we don't need to have a scheduled - // write until the next change. - removeMessages(MSG_WRITE_STATISTICS); - - FileOutputStream fos = null; - try { - fos = mStatisticsFile.startWrite(); - Parcel out = Parcel.obtain(); - final int N = mDayStats.length; - for (int i=0; i<N; i++) { - DayStats ds = mDayStats[i]; - if (ds == null) { - break; - } - out.writeInt(STATISTICS_FILE_ITEM); - out.writeInt(ds.day); - out.writeInt(ds.successCount); - out.writeLong(ds.successTime); - out.writeInt(ds.failureCount); - out.writeLong(ds.failureTime); - } - out.writeInt(STATISTICS_FILE_END); - fos.write(out.marshall()); - out.recycle(); - - mStatisticsFile.finishWrite(fos); - } catch (java.io.IOException e1) { - Log.w(TAG, "Error writing stats", e1); - if (fos != null) { - mStatisticsFile.failWrite(fos); - } - } - } -} diff --git a/core/java/android/os/SchedulingPolicyService.java b/core/java/android/os/SchedulingPolicyService.java deleted file mode 100644 index a3fede6..0000000 --- a/core/java/android/os/SchedulingPolicyService.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.os; - -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.Binder; -import android.os.Process; -import android.util.Log; - -/** - * The implementation of the scheduling policy service interface. - * - * @hide - */ -public class SchedulingPolicyService extends ISchedulingPolicyService.Stub { - - private static final String TAG = "SchedulingPolicyService"; - - // Minimum and maximum values allowed for requestPriority parameter prio - private static final int PRIORITY_MIN = 1; - private static final int PRIORITY_MAX = 3; - - public SchedulingPolicyService() { - } - - public int requestPriority(int pid, int tid, int prio) { - //Log.i(TAG, "requestPriority(pid=" + pid + ", tid=" + tid + ", prio=" + prio + ")"); - - // Verify that caller is mediaserver, priority is in range, and that the - // callback thread specified by app belongs to the app that called mediaserver. - // Once we've verified that the caller is mediaserver, we can trust the pid but - // we can't trust the tid. No need to explicitly check for pid == 0 || tid == 0, - // since if not the case then the getThreadGroupLeader() test will also fail. - if (Binder.getCallingUid() != Process.MEDIA_UID || prio < PRIORITY_MIN || - prio > PRIORITY_MAX || Process.getThreadGroupLeader(tid) != pid) { - return PackageManager.PERMISSION_DENIED; - } - try { - // make good use of our CAP_SYS_NICE capability - Process.setThreadGroup(tid, Binder.getCallingPid() == pid ? - Process.THREAD_GROUP_AUDIO_SYS : Process.THREAD_GROUP_AUDIO_APP); - // must be in this order or it fails the schedulability constraint - Process.setThreadScheduler(tid, Process.SCHED_FIFO, prio); - } catch (RuntimeException e) { - return PackageManager.PERMISSION_DENIED; - } - return PackageManager.PERMISSION_GRANTED; - } - -} diff --git a/core/java/android/server/package.html b/core/java/android/server/package.html deleted file mode 100644 index c9f96a6..0000000 --- a/core/java/android/server/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<body> - -{@hide} - -</body> diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java deleted file mode 100644 index 46f2723..0000000 --- a/core/java/android/server/search/SearchManagerService.java +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.server.search; - -import com.android.internal.content.PackageMonitor; -import com.android.internal.util.IndentingPrintWriter; - -import android.app.ActivityManager; -import android.app.ActivityManagerNative; -import android.app.AppGlobals; -import android.app.ISearchManager; -import android.app.SearchManager; -import android.app.SearchableInfo; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.IPackageManager; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.database.ContentObserver; -import android.os.Binder; -import android.os.Process; -import android.os.RemoteException; -import android.os.UserHandle; -import android.os.UserManager; -import android.provider.Settings; -import android.util.Log; -import android.util.Slog; -import android.util.SparseArray; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.List; - -/** - * The search manager service handles the search UI, and maintains a registry of searchable - * activities. - */ -public class SearchManagerService extends ISearchManager.Stub { - - // general debugging support - private static final String TAG = "SearchManagerService"; - - // Context that the service is running in. - private final Context mContext; - - // This field is initialized lazily in getSearchables(), and then never modified. - private final SparseArray<Searchables> mSearchables = new SparseArray<Searchables>(); - - /** - * Initializes the Search Manager service in the provided system context. - * Only one instance of this object should be created! - * - * @param context to use for accessing DB, window manager, etc. - */ - public SearchManagerService(Context context) { - mContext = context; - mContext.registerReceiver(new BootCompletedReceiver(), - new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); - mContext.registerReceiver(new UserReceiver(), - new IntentFilter(Intent.ACTION_USER_REMOVED)); - new MyPackageMonitor().register(context, null, UserHandle.ALL, true); - } - - private Searchables getSearchables(int userId) { - long origId = Binder.clearCallingIdentity(); - try { - boolean userExists = ((UserManager) mContext.getSystemService(Context.USER_SERVICE)) - .getUserInfo(userId) != null; - if (!userExists) return null; - } finally { - Binder.restoreCallingIdentity(origId); - } - synchronized (mSearchables) { - Searchables searchables = mSearchables.get(userId); - - if (searchables == null) { - //Log.i(TAG, "Building list of searchable activities for userId=" + userId); - searchables = new Searchables(mContext, userId); - searchables.buildSearchableList(); - mSearchables.append(userId, searchables); - } - return searchables; - } - } - - private void onUserRemoved(int userId) { - if (userId != UserHandle.USER_OWNER) { - synchronized (mSearchables) { - mSearchables.remove(userId); - } - } - } - - /** - * Creates the initial searchables list after boot. - */ - private final class BootCompletedReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - new Thread() { - @Override - public void run() { - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - mContext.unregisterReceiver(BootCompletedReceiver.this); - getSearchables(0); - } - }.start(); - } - } - - private final class UserReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_OWNER)); - } - } - - /** - * Refreshes the "searchables" list when packages are added/removed. - */ - class MyPackageMonitor extends PackageMonitor { - - @Override - public void onSomePackagesChanged() { - updateSearchables(); - } - - @Override - public void onPackageModified(String pkg) { - updateSearchables(); - } - - private void updateSearchables() { - final int changingUserId = getChangingUserId(); - synchronized (mSearchables) { - // Update list of searchable activities - for (int i = 0; i < mSearchables.size(); i++) { - if (changingUserId == mSearchables.keyAt(i)) { - getSearchables(mSearchables.keyAt(i)).buildSearchableList(); - break; - } - } - } - // Inform all listeners that the list of searchables has been updated. - Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING - | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, new UserHandle(changingUserId)); - } - } - - class GlobalSearchProviderObserver extends ContentObserver { - private final ContentResolver mResolver; - - public GlobalSearchProviderObserver(ContentResolver resolver) { - super(null); - mResolver = resolver; - mResolver.registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY), - false /* notifyDescendants */, - this); - } - - @Override - public void onChange(boolean selfChange) { - synchronized (mSearchables) { - for (int i = 0; i < mSearchables.size(); i++) { - getSearchables(mSearchables.keyAt(i)).buildSearchableList(); - } - } - Intent intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); - } - - } - - // - // Searchable activities API - // - - /** - * Returns the SearchableInfo for a given activity. - * - * @param launchActivity The activity from which we're launching this search. - * @return Returns a SearchableInfo record describing the parameters of the search, - * or null if no searchable metadata was available. - */ - public SearchableInfo getSearchableInfo(final ComponentName launchActivity) { - if (launchActivity == null) { - Log.e(TAG, "getSearchableInfo(), activity == null"); - return null; - } - return getSearchables(UserHandle.getCallingUserId()).getSearchableInfo(launchActivity); - } - - /** - * Returns a list of the searchable activities that can be included in global search. - */ - public List<SearchableInfo> getSearchablesInGlobalSearch() { - return getSearchables(UserHandle.getCallingUserId()).getSearchablesInGlobalSearchList(); - } - - public List<ResolveInfo> getGlobalSearchActivities() { - return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivities(); - } - - /** - * Gets the name of the global search activity. - */ - public ComponentName getGlobalSearchActivity() { - return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivity(); - } - - /** - * Gets the name of the web search activity. - */ - public ComponentName getWebSearchActivity() { - return getSearchables(UserHandle.getCallingUserId()).getWebSearchActivity(); - } - - @Override - public ComponentName getAssistIntent(int userHandle) { - try { - if (userHandle != UserHandle.getCallingUserId()) { - // Requesting a different user, make sure that they have the permission - if (ActivityManager.checkComponentPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, - Binder.getCallingUid(), -1, true) - == PackageManager.PERMISSION_GRANTED) { - // Translate to the current user id, if caller wasn't aware - if (userHandle == UserHandle.USER_CURRENT) { - long identity = Binder.clearCallingIdentity(); - userHandle = ActivityManagerNative.getDefault().getCurrentUser().id; - Binder.restoreCallingIdentity(identity); - } - } else { - String msg = "Permission Denial: " - + "Request to getAssistIntent for " + userHandle - + " but is calling from user " + UserHandle.getCallingUserId() - + "; this requires " - + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; - Slog.w(TAG, msg); - return null; - } - } - IPackageManager pm = AppGlobals.getPackageManager(); - Intent assistIntent = new Intent(Intent.ACTION_ASSIST); - ResolveInfo info = - pm.resolveIntent(assistIntent, - assistIntent.resolveTypeIfNeeded(mContext.getContentResolver()), - PackageManager.MATCH_DEFAULT_ONLY, userHandle); - if (info != null) { - return new ComponentName( - info.activityInfo.applicationInfo.packageName, - info.activityInfo.name); - } - } catch (RemoteException re) { - // Local call - Log.e(TAG, "RemoteException in getAssistIntent: " + re); - } catch (Exception e) { - Log.e(TAG, "Exception in getAssistIntent: " + e); - } - return null; - } - - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); - - IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); - synchronized (mSearchables) { - for (int i = 0; i < mSearchables.size(); i++) { - ipw.print("\nUser: "); ipw.println(mSearchables.keyAt(i)); - ipw.increaseIndent(); - mSearchables.valueAt(i).dump(fd, ipw, args); - ipw.decreaseIndent(); - } - } - } -} diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java deleted file mode 100644 index a0095d6..0000000 --- a/core/java/android/server/search/Searchables.java +++ /dev/null @@ -1,464 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.server.search; - -import android.app.AppGlobals; -import android.app.SearchManager; -import android.app.SearchableInfo; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageManager; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.os.Binder; -import android.os.Bundle; -import android.os.RemoteException; -import android.provider.Settings; -import android.text.TextUtils; -import android.util.Log; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; - -/** - * This class maintains the information about all searchable activities. - * This is a hidden class. - */ -public class Searchables { - - private static final String LOG_TAG = "Searchables"; - - // static strings used for XML lookups, etc. - // TODO how should these be documented for the developer, in a more structured way than - // the current long wordy javadoc in SearchManager.java ? - private static final String MD_LABEL_DEFAULT_SEARCHABLE = "android.app.default_searchable"; - private static final String MD_SEARCHABLE_SYSTEM_SEARCH = "*"; - - private Context mContext; - - private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null; - private ArrayList<SearchableInfo> mSearchablesList = null; - private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null; - // Contains all installed activities that handle the global search - // intent. - private List<ResolveInfo> mGlobalSearchActivities; - private ComponentName mCurrentGlobalSearchActivity = null; - private ComponentName mWebSearchActivity = null; - - public static String GOOGLE_SEARCH_COMPONENT_NAME = - "com.android.googlesearch/.GoogleSearch"; - public static String ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME = - "com.google.android.providers.enhancedgooglesearch/.Launcher"; - - // Cache the package manager instance - final private IPackageManager mPm; - // User for which this Searchables caches information - private int mUserId; - - /** - * - * @param context Context to use for looking up activities etc. - */ - public Searchables (Context context, int userId) { - mContext = context; - mUserId = userId; - mPm = AppGlobals.getPackageManager(); - } - - /** - * Look up, or construct, based on the activity. - * - * The activities fall into three cases, based on meta-data found in - * the manifest entry: - * <ol> - * <li>The activity itself implements search. This is indicated by the - * presence of a "android.app.searchable" meta-data attribute. - * The value is a reference to an XML file containing search information.</li> - * <li>A related activity implements search. This is indicated by the - * presence of a "android.app.default_searchable" meta-data attribute. - * The value is a string naming the activity implementing search. In this - * case the factory will "redirect" and return the searchable data.</li> - * <li>No searchability data is provided. We return null here and other - * code will insert the "default" (e.g. contacts) search. - * - * TODO: cache the result in the map, and check the map first. - * TODO: it might make sense to implement the searchable reference as - * an application meta-data entry. This way we don't have to pepper each - * and every activity. - * TODO: can we skip the constructor step if it's a non-searchable? - * TODO: does it make sense to plug the default into a slot here for - * automatic return? Probably not, but it's one way to do it. - * - * @param activity The name of the current activity, or null if the - * activity does not define any explicit searchable metadata. - */ - public SearchableInfo getSearchableInfo(ComponentName activity) { - // Step 1. Is the result already hashed? (case 1) - SearchableInfo result; - synchronized (this) { - result = mSearchablesMap.get(activity); - if (result != null) return result; - } - - // Step 2. See if the current activity references a searchable. - // Note: Conceptually, this could be a while(true) loop, but there's - // no point in implementing reference chaining here and risking a loop. - // References must point directly to searchable activities. - - ActivityInfo ai = null; - try { - ai = mPm.getActivityInfo(activity, PackageManager.GET_META_DATA, mUserId); - } catch (RemoteException re) { - Log.e(LOG_TAG, "Error getting activity info " + re); - return null; - } - String refActivityName = null; - - // First look for activity-specific reference - Bundle md = ai.metaData; - if (md != null) { - refActivityName = md.getString(MD_LABEL_DEFAULT_SEARCHABLE); - } - // If not found, try for app-wide reference - if (refActivityName == null) { - md = ai.applicationInfo.metaData; - if (md != null) { - refActivityName = md.getString(MD_LABEL_DEFAULT_SEARCHABLE); - } - } - - // Irrespective of source, if a reference was found, follow it. - if (refActivityName != null) - { - // This value is deprecated, return null - if (refActivityName.equals(MD_SEARCHABLE_SYSTEM_SEARCH)) { - return null; - } - String pkg = activity.getPackageName(); - ComponentName referredActivity; - if (refActivityName.charAt(0) == '.') { - referredActivity = new ComponentName(pkg, pkg + refActivityName); - } else { - referredActivity = new ComponentName(pkg, refActivityName); - } - - // Now try the referred activity, and if found, cache - // it against the original name so we can skip the check - synchronized (this) { - result = mSearchablesMap.get(referredActivity); - if (result != null) { - mSearchablesMap.put(activity, result); - return result; - } - } - } - - // Step 3. None found. Return null. - return null; - - } - - /** - * Builds an entire list (suitable for display) of - * activities that are searchable, by iterating the entire set of - * ACTION_SEARCH & ACTION_WEB_SEARCH intents. - * - * Also clears the hash of all activities -> searches which will - * refill as the user clicks "search". - * - * This should only be done at startup and again if we know that the - * list has changed. - * - * TODO: every activity that provides a ACTION_SEARCH intent should - * also provide searchability meta-data. There are a bunch of checks here - * that, if data is not found, silently skip to the next activity. This - * won't help a developer trying to figure out why their activity isn't - * showing up in the list, but an exception here is too rough. I would - * like to find a better notification mechanism. - * - * TODO: sort the list somehow? UI choice. - */ - public void buildSearchableList() { - // These will become the new values at the end of the method - HashMap<ComponentName, SearchableInfo> newSearchablesMap - = new HashMap<ComponentName, SearchableInfo>(); - ArrayList<SearchableInfo> newSearchablesList - = new ArrayList<SearchableInfo>(); - ArrayList<SearchableInfo> newSearchablesInGlobalSearchList - = new ArrayList<SearchableInfo>(); - - // Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers. - List<ResolveInfo> searchList; - final Intent intent = new Intent(Intent.ACTION_SEARCH); - - long ident = Binder.clearCallingIdentity(); - try { - searchList = queryIntentActivities(intent, PackageManager.GET_META_DATA); - - List<ResolveInfo> webSearchInfoList; - final Intent webSearchIntent = new Intent(Intent.ACTION_WEB_SEARCH); - webSearchInfoList = queryIntentActivities(webSearchIntent, PackageManager.GET_META_DATA); - - // analyze each one, generate a Searchables record, and record - if (searchList != null || webSearchInfoList != null) { - int search_count = (searchList == null ? 0 : searchList.size()); - int web_search_count = (webSearchInfoList == null ? 0 : webSearchInfoList.size()); - int count = search_count + web_search_count; - for (int ii = 0; ii < count; ii++) { - // for each component, try to find metadata - ResolveInfo info = (ii < search_count) - ? searchList.get(ii) - : webSearchInfoList.get(ii - search_count); - ActivityInfo ai = info.activityInfo; - // Check first to avoid duplicate entries. - if (newSearchablesMap.get(new ComponentName(ai.packageName, ai.name)) == null) { - SearchableInfo searchable = SearchableInfo.getActivityMetaData(mContext, ai, - mUserId); - if (searchable != null) { - newSearchablesList.add(searchable); - newSearchablesMap.put(searchable.getSearchActivity(), searchable); - if (searchable.shouldIncludeInGlobalSearch()) { - newSearchablesInGlobalSearchList.add(searchable); - } - } - } - } - } - - List<ResolveInfo> newGlobalSearchActivities = findGlobalSearchActivities(); - - // Find the global search activity - ComponentName newGlobalSearchActivity = findGlobalSearchActivity( - newGlobalSearchActivities); - - // Find the web search activity - ComponentName newWebSearchActivity = findWebSearchActivity(newGlobalSearchActivity); - - // Store a consistent set of new values - synchronized (this) { - mSearchablesMap = newSearchablesMap; - mSearchablesList = newSearchablesList; - mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList; - mGlobalSearchActivities = newGlobalSearchActivities; - mCurrentGlobalSearchActivity = newGlobalSearchActivity; - mWebSearchActivity = newWebSearchActivity; - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - /** - * Returns a sorted list of installed search providers as per - * the following heuristics: - * - * (a) System apps are given priority over non system apps. - * (b) Among system apps and non system apps, the relative ordering - * is defined by their declared priority. - */ - private List<ResolveInfo> findGlobalSearchActivities() { - // Step 1 : Query the package manager for a list - // of activities that can handle the GLOBAL_SEARCH intent. - Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); - List<ResolveInfo> activities = - queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); - if (activities != null && !activities.isEmpty()) { - // Step 2: Rank matching activities according to our heuristics. - Collections.sort(activities, GLOBAL_SEARCH_RANKER); - } - - return activities; - } - - /** - * Finds the global search activity. - */ - private ComponentName findGlobalSearchActivity(List<ResolveInfo> installed) { - // Fetch the global search provider from the system settings, - // and if it's still installed, return it. - final String searchProviderSetting = getGlobalSearchProviderSetting(); - if (!TextUtils.isEmpty(searchProviderSetting)) { - final ComponentName globalSearchComponent = ComponentName.unflattenFromString( - searchProviderSetting); - if (globalSearchComponent != null && isInstalled(globalSearchComponent)) { - return globalSearchComponent; - } - } - - return getDefaultGlobalSearchProvider(installed); - } - - /** - * Checks whether the global search provider with a given - * component name is installed on the system or not. This deals with - * cases such as the removal of an installed provider. - */ - private boolean isInstalled(ComponentName globalSearch) { - Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); - intent.setComponent(globalSearch); - - List<ResolveInfo> activities = queryIntentActivities(intent, - PackageManager.MATCH_DEFAULT_ONLY); - if (activities != null && !activities.isEmpty()) { - return true; - } - - return false; - } - - private static final Comparator<ResolveInfo> GLOBAL_SEARCH_RANKER = - new Comparator<ResolveInfo>() { - @Override - public int compare(ResolveInfo lhs, ResolveInfo rhs) { - if (lhs == rhs) { - return 0; - } - boolean lhsSystem = isSystemApp(lhs); - boolean rhsSystem = isSystemApp(rhs); - - if (lhsSystem && !rhsSystem) { - return -1; - } else if (rhsSystem && !lhsSystem) { - return 1; - } else { - // Either both system engines, or both non system - // engines. - // - // Note, this isn't a typo. Higher priority numbers imply - // higher priority, but are "lower" in the sort order. - return rhs.priority - lhs.priority; - } - } - }; - - /** - * @return true iff. the resolve info corresponds to a system application. - */ - private static final boolean isSystemApp(ResolveInfo res) { - return (res.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - } - - /** - * Returns the highest ranked search provider as per the - * ranking defined in {@link #getGlobalSearchActivities()}. - */ - private ComponentName getDefaultGlobalSearchProvider(List<ResolveInfo> providerList) { - if (providerList != null && !providerList.isEmpty()) { - ActivityInfo ai = providerList.get(0).activityInfo; - return new ComponentName(ai.packageName, ai.name); - } - - Log.w(LOG_TAG, "No global search activity found"); - return null; - } - - private String getGlobalSearchProviderSetting() { - return Settings.Secure.getString(mContext.getContentResolver(), - Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY); - } - - /** - * Finds the web search activity. - * - * Only looks in the package of the global search activity. - */ - private ComponentName findWebSearchActivity(ComponentName globalSearchActivity) { - if (globalSearchActivity == null) { - return null; - } - Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); - intent.setPackage(globalSearchActivity.getPackageName()); - List<ResolveInfo> activities = - queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); - - if (activities != null && !activities.isEmpty()) { - ActivityInfo ai = activities.get(0).activityInfo; - // TODO: do some sanity checks here? - return new ComponentName(ai.packageName, ai.name); - } - Log.w(LOG_TAG, "No web search activity found"); - return null; - } - - private List<ResolveInfo> queryIntentActivities(Intent intent, int flags) { - List<ResolveInfo> activities = null; - try { - activities = - mPm.queryIntentActivities(intent, - intent.resolveTypeIfNeeded(mContext.getContentResolver()), - flags, mUserId); - } catch (RemoteException re) { - // Local call - } - return activities; - } - - /** - * Returns the list of searchable activities. - */ - public synchronized ArrayList<SearchableInfo> getSearchablesList() { - ArrayList<SearchableInfo> result = new ArrayList<SearchableInfo>(mSearchablesList); - return result; - } - - /** - * Returns a list of the searchable activities that can be included in global search. - */ - public synchronized ArrayList<SearchableInfo> getSearchablesInGlobalSearchList() { - return new ArrayList<SearchableInfo>(mSearchablesInGlobalSearchList); - } - - /** - * Returns a list of activities that handle the global search intent. - */ - public synchronized ArrayList<ResolveInfo> getGlobalSearchActivities() { - return new ArrayList<ResolveInfo>(mGlobalSearchActivities); - } - - /** - * Gets the name of the global search activity. - */ - public synchronized ComponentName getGlobalSearchActivity() { - return mCurrentGlobalSearchActivity; - } - - /** - * Gets the name of the web search activity. - */ - public synchronized ComponentName getWebSearchActivity() { - return mWebSearchActivity; - } - - void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("Searchable authorities:"); - synchronized (this) { - if (mSearchablesList != null) { - for (SearchableInfo info: mSearchablesList) { - pw.print(" "); pw.println(info.getSuggestAuthority()); - } - } - } - } -} diff --git a/core/java/android/server/search/package.html b/core/java/android/server/search/package.html deleted file mode 100644 index c9f96a6..0000000 --- a/core/java/android/server/search/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<body> - -{@hide} - -</body> diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 907b52a..555c7c2 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -123,14 +123,14 @@ public class LockPatternUtils { */ public static final int ID_DEFAULT_STATUS_WIDGET = -2; - protected final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently"; - protected final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline"; - protected final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen"; + public final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently"; + public final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline"; + public final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen"; public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type"; public static final String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate"; - protected final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt"; - protected final static String DISABLE_LOCKSCREEN_KEY = "lockscreen.disabled"; - protected final static String LOCKSCREEN_OPTIONS = "lockscreen.options"; + public final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt"; + public final static String DISABLE_LOCKSCREEN_KEY = "lockscreen.disabled"; + public final static String LOCKSCREEN_OPTIONS = "lockscreen.options"; public final static String LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK = "lockscreen.biometric_weak_fallback"; public final static String BIOMETRIC_WEAK_EVER_CHOSEN_KEY @@ -138,7 +138,7 @@ public class LockPatternUtils { public final static String LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS = "lockscreen.power_button_instantly_locks"; - protected final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory"; + public final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory"; private final Context mContext; private final ContentResolver mContentResolver; diff --git a/core/java/com/android/internal/widget/LockSettingsService.java b/core/java/com/android/internal/widget/LockSettingsService.java deleted file mode 100644 index 4ecbd16..0000000 --- a/core/java/com/android/internal/widget/LockSettingsService.java +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.widget; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.os.Binder; -import android.os.Environment; -import android.os.RemoteException; -import android.os.SystemProperties; -import android.os.UserHandle; -import android.provider.Settings; -import android.provider.Settings.Secure; -import android.text.TextUtils; -import android.util.Slog; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.util.Arrays; - -/** - * Keeps the lock pattern/password data and related settings for each user. - * Used by LockPatternUtils. Needs to be a service because Settings app also needs - * to be able to save lockscreen information for secondary users. - * @hide - */ -public class LockSettingsService extends ILockSettings.Stub { - - private final DatabaseHelper mOpenHelper; - private static final String TAG = "LockSettingsService"; - - private static final String TABLE = "locksettings"; - private static final String COLUMN_KEY = "name"; - private static final String COLUMN_USERID = "user"; - private static final String COLUMN_VALUE = "value"; - - private static final String[] COLUMNS_FOR_QUERY = { - COLUMN_VALUE - }; - - private static final String SYSTEM_DIRECTORY = "/system/"; - private static final String LOCK_PATTERN_FILE = "gesture.key"; - private static final String LOCK_PASSWORD_FILE = "password.key"; - - private final Context mContext; - - public LockSettingsService(Context context) { - mContext = context; - // Open the database - mOpenHelper = new DatabaseHelper(mContext); - } - - public void systemReady() { - migrateOldData(); - } - - private void migrateOldData() { - try { - if (getString("migrated", null, 0) != null) { - // Already migrated - return; - } - - final ContentResolver cr = mContext.getContentResolver(); - for (String validSetting : VALID_SETTINGS) { - String value = Settings.Secure.getString(cr, validSetting); - if (value != null) { - setString(validSetting, value, 0); - } - } - // No need to move the password / pattern files. They're already in the right place. - setString("migrated", "true", 0); - Slog.i(TAG, "Migrated lock settings to new location"); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to migrate old data"); - } - } - - private static final void checkWritePermission(int userId) { - final int callingUid = Binder.getCallingUid(); - if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID) { - throw new SecurityException("uid=" + callingUid - + " not authorized to write lock settings"); - } - } - - private static final void checkPasswordReadPermission(int userId) { - final int callingUid = Binder.getCallingUid(); - if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID) { - throw new SecurityException("uid=" + callingUid - + " not authorized to read lock password"); - } - } - - private static final void checkReadPermission(int userId) { - final int callingUid = Binder.getCallingUid(); - if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID - && UserHandle.getUserId(callingUid) != userId) { - throw new SecurityException("uid=" + callingUid - + " not authorized to read settings of user " + userId); - } - } - - @Override - public void setBoolean(String key, boolean value, int userId) throws RemoteException { - checkWritePermission(userId); - - writeToDb(key, value ? "1" : "0", userId); - } - - @Override - public void setLong(String key, long value, int userId) throws RemoteException { - checkWritePermission(userId); - - writeToDb(key, Long.toString(value), userId); - } - - @Override - public void setString(String key, String value, int userId) throws RemoteException { - checkWritePermission(userId); - - writeToDb(key, value, userId); - } - - @Override - public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException { - //checkReadPermission(userId); - - String value = readFromDb(key, null, userId); - return TextUtils.isEmpty(value) ? - defaultValue : (value.equals("1") || value.equals("true")); - } - - @Override - public long getLong(String key, long defaultValue, int userId) throws RemoteException { - //checkReadPermission(userId); - - String value = readFromDb(key, null, userId); - return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value); - } - - @Override - public String getString(String key, String defaultValue, int userId) throws RemoteException { - //checkReadPermission(userId); - - return readFromDb(key, defaultValue, userId); - } - - private String getLockPatternFilename(int userId) { - String dataSystemDirectory = - android.os.Environment.getDataDirectory().getAbsolutePath() + - SYSTEM_DIRECTORY; - if (userId == 0) { - // Leave it in the same place for user 0 - return dataSystemDirectory + LOCK_PATTERN_FILE; - } else { - return new File(Environment.getUserSystemDirectory(userId), LOCK_PATTERN_FILE) - .getAbsolutePath(); - } - } - - private String getLockPasswordFilename(int userId) { - String dataSystemDirectory = - android.os.Environment.getDataDirectory().getAbsolutePath() + - SYSTEM_DIRECTORY; - if (userId == 0) { - // Leave it in the same place for user 0 - return dataSystemDirectory + LOCK_PASSWORD_FILE; - } else { - return new File(Environment.getUserSystemDirectory(userId), LOCK_PASSWORD_FILE) - .getAbsolutePath(); - } - } - - @Override - public boolean havePassword(int userId) throws RemoteException { - // Do we need a permissions check here? - - return new File(getLockPasswordFilename(userId)).length() > 0; - } - - @Override - public boolean havePattern(int userId) throws RemoteException { - // Do we need a permissions check here? - - return new File(getLockPatternFilename(userId)).length() > 0; - } - - @Override - public void setLockPattern(byte[] hash, int userId) throws RemoteException { - checkWritePermission(userId); - - writeFile(getLockPatternFilename(userId), hash); - } - - @Override - public boolean checkPattern(byte[] hash, int userId) throws RemoteException { - checkPasswordReadPermission(userId); - try { - // Read all the bytes from the file - RandomAccessFile raf = new RandomAccessFile(getLockPatternFilename(userId), "r"); - final byte[] stored = new byte[(int) raf.length()]; - int got = raf.read(stored, 0, stored.length); - raf.close(); - if (got <= 0) { - return true; - } - // Compare the hash from the file with the entered pattern's hash - return Arrays.equals(stored, hash); - } catch (FileNotFoundException fnfe) { - Slog.e(TAG, "Cannot read file " + fnfe); - return true; - } catch (IOException ioe) { - Slog.e(TAG, "Cannot read file " + ioe); - return true; - } - } - - @Override - public void setLockPassword(byte[] hash, int userId) throws RemoteException { - checkWritePermission(userId); - - writeFile(getLockPasswordFilename(userId), hash); - } - - @Override - public boolean checkPassword(byte[] hash, int userId) throws RemoteException { - checkPasswordReadPermission(userId); - - try { - // Read all the bytes from the file - RandomAccessFile raf = new RandomAccessFile(getLockPasswordFilename(userId), "r"); - final byte[] stored = new byte[(int) raf.length()]; - int got = raf.read(stored, 0, stored.length); - raf.close(); - if (got <= 0) { - return true; - } - // Compare the hash from the file with the entered password's hash - return Arrays.equals(stored, hash); - } catch (FileNotFoundException fnfe) { - Slog.e(TAG, "Cannot read file " + fnfe); - return true; - } catch (IOException ioe) { - Slog.e(TAG, "Cannot read file " + ioe); - return true; - } - } - - @Override - public void removeUser(int userId) { - checkWritePermission(userId); - - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - try { - File file = new File(getLockPasswordFilename(userId)); - if (file.exists()) { - file.delete(); - } - file = new File(getLockPatternFilename(userId)); - if (file.exists()) { - file.delete(); - } - - db.beginTransaction(); - db.delete(TABLE, COLUMN_USERID + "='" + userId + "'", null); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - - private void writeFile(String name, byte[] hash) { - try { - // Write the hash to file - RandomAccessFile raf = new RandomAccessFile(name, "rw"); - // Truncate the file if pattern is null, to clear the lock - if (hash == null || hash.length == 0) { - raf.setLength(0); - } else { - raf.write(hash, 0, hash.length); - } - raf.close(); - } catch (IOException ioe) { - Slog.e(TAG, "Error writing to file " + ioe); - } - } - - private void writeToDb(String key, String value, int userId) { - writeToDb(mOpenHelper.getWritableDatabase(), key, value, userId); - } - - private void writeToDb(SQLiteDatabase db, String key, String value, int userId) { - ContentValues cv = new ContentValues(); - cv.put(COLUMN_KEY, key); - cv.put(COLUMN_USERID, userId); - cv.put(COLUMN_VALUE, value); - - db.beginTransaction(); - try { - db.delete(TABLE, COLUMN_KEY + "=? AND " + COLUMN_USERID + "=?", - new String[] {key, Integer.toString(userId)}); - db.insert(TABLE, null, cv); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - - private String readFromDb(String key, String defaultValue, int userId) { - Cursor cursor; - String result = defaultValue; - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); - if ((cursor = db.query(TABLE, COLUMNS_FOR_QUERY, - COLUMN_USERID + "=? AND " + COLUMN_KEY + "=?", - new String[] { Integer.toString(userId), key }, - null, null, null)) != null) { - if (cursor.moveToFirst()) { - result = cursor.getString(0); - } - cursor.close(); - } - return result; - } - - class DatabaseHelper extends SQLiteOpenHelper { - private static final String TAG = "LockSettingsDB"; - private static final String DATABASE_NAME = "locksettings.db"; - - private static final int DATABASE_VERSION = 1; - - public DatabaseHelper(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - setWriteAheadLoggingEnabled(true); - } - - private void createTable(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + TABLE + " (" + - "_id INTEGER PRIMARY KEY AUTOINCREMENT," + - COLUMN_KEY + " TEXT," + - COLUMN_USERID + " INTEGER," + - COLUMN_VALUE + " TEXT" + - ");"); - } - - @Override - public void onCreate(SQLiteDatabase db) { - createTable(db); - initializeDefaults(db); - } - - private void initializeDefaults(SQLiteDatabase db) { - // Get the lockscreen default from a system property, if available - boolean lockScreenDisable = SystemProperties.getBoolean("ro.lockscreen.disable.default", - false); - if (lockScreenDisable) { - writeToDb(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0); - } - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) { - // Nothing yet - } - } - - private static final String[] VALID_SETTINGS = new String[] { - LockPatternUtils.LOCKOUT_PERMANENT_KEY, - LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE, - LockPatternUtils.PATTERN_EVER_CHOSEN_KEY, - LockPatternUtils.PASSWORD_TYPE_KEY, - LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY, - LockPatternUtils.LOCK_PASSWORD_SALT_KEY, - LockPatternUtils.DISABLE_LOCKSCREEN_KEY, - LockPatternUtils.LOCKSCREEN_OPTIONS, - LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, - LockPatternUtils.BIOMETRIC_WEAK_EVER_CHOSEN_KEY, - LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, - LockPatternUtils.PASSWORD_HISTORY_KEY, - Secure.LOCK_PATTERN_ENABLED, - Secure.LOCK_BIOMETRIC_WEAK_FLAGS, - Secure.LOCK_PATTERN_VISIBLE, - Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED - }; -} diff --git a/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java b/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java deleted file mode 100644 index 84c9957..0000000 --- a/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.accounts; - -import android.app.Notification; -import android.content.Context; -import android.content.pm.PackageManager; -import android.content.pm.RegisteredServicesCache.ServiceInfo; -import android.content.pm.RegisteredServicesCacheListener; -import android.os.Bundle; -import android.os.Handler; -import android.os.UserHandle; -import android.test.AndroidTestCase; -import android.test.IsolatedContext; -import android.test.RenamingDelegatingContext; -import android.test.mock.MockContentResolver; -import android.test.mock.MockContext; -import android.test.mock.MockPackageManager; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; - -public class AccountManagerServiceTest extends AndroidTestCase { - private AccountManagerService mAms; - - @Override - protected void setUp() throws Exception { - final String filenamePrefix = "test."; - MockContentResolver resolver = new MockContentResolver(); - RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext( - new MyMockContext(), // The context that most methods are delegated to - getContext(), // The context that file methods are delegated to - filenamePrefix); - Context context = new IsolatedContext(resolver, targetContextWrapper); - setContext(context); - mAms = new MyAccountManagerService(getContext(), - new MyMockPackageManager(), new MockAccountAuthenticatorCache()); - } - - public class AccountSorter implements Comparator<Account> { - public int compare(Account object1, Account object2) { - if (object1 == object2) return 0; - if (object1 == null) return 1; - if (object2 == null) return -1; - int result = object1.type.compareTo(object2.type); - if (result != 0) return result; - return object1.name.compareTo(object2.name); - } - } - - public void testCheckAddAccount() throws Exception { - Account a11 = new Account("account1", "type1"); - Account a21 = new Account("account2", "type1"); - Account a31 = new Account("account3", "type1"); - Account a12 = new Account("account1", "type2"); - Account a22 = new Account("account2", "type2"); - Account a32 = new Account("account3", "type2"); - mAms.addAccount(a11, "p11", null); - mAms.addAccount(a12, "p12", null); - mAms.addAccount(a21, "p21", null); - mAms.addAccount(a22, "p22", null); - mAms.addAccount(a31, "p31", null); - mAms.addAccount(a32, "p32", null); - - Account[] accounts = mAms.getAccounts(null); - Arrays.sort(accounts, new AccountSorter()); - assertEquals(6, accounts.length); - assertEquals(a11, accounts[0]); - assertEquals(a21, accounts[1]); - assertEquals(a31, accounts[2]); - assertEquals(a12, accounts[3]); - assertEquals(a22, accounts[4]); - assertEquals(a32, accounts[5]); - - accounts = mAms.getAccounts("type1" ); - Arrays.sort(accounts, new AccountSorter()); - assertEquals(3, accounts.length); - assertEquals(a11, accounts[0]); - assertEquals(a21, accounts[1]); - assertEquals(a31, accounts[2]); - - mAms.removeAccountInternal(a21); - - accounts = mAms.getAccounts("type1" ); - Arrays.sort(accounts, new AccountSorter()); - assertEquals(2, accounts.length); - assertEquals(a11, accounts[0]); - assertEquals(a31, accounts[1]); - } - - public void testPasswords() throws Exception { - Account a11 = new Account("account1", "type1"); - Account a12 = new Account("account1", "type2"); - mAms.addAccount(a11, "p11", null); - mAms.addAccount(a12, "p12", null); - - assertEquals("p11", mAms.getPassword(a11)); - assertEquals("p12", mAms.getPassword(a12)); - - mAms.setPassword(a11, "p11b"); - - assertEquals("p11b", mAms.getPassword(a11)); - assertEquals("p12", mAms.getPassword(a12)); - } - - public void testUserdata() throws Exception { - Account a11 = new Account("account1", "type1"); - Bundle u11 = new Bundle(); - u11.putString("a", "a_a11"); - u11.putString("b", "b_a11"); - u11.putString("c", "c_a11"); - Account a12 = new Account("account1", "type2"); - Bundle u12 = new Bundle(); - u12.putString("a", "a_a12"); - u12.putString("b", "b_a12"); - u12.putString("c", "c_a12"); - mAms.addAccount(a11, "p11", u11); - mAms.addAccount(a12, "p12", u12); - - assertEquals("a_a11", mAms.getUserData(a11, "a")); - assertEquals("b_a11", mAms.getUserData(a11, "b")); - assertEquals("c_a11", mAms.getUserData(a11, "c")); - assertEquals("a_a12", mAms.getUserData(a12, "a")); - assertEquals("b_a12", mAms.getUserData(a12, "b")); - assertEquals("c_a12", mAms.getUserData(a12, "c")); - - mAms.setUserData(a11, "b", "b_a11b"); - mAms.setUserData(a12, "c", null); - - assertEquals("a_a11", mAms.getUserData(a11, "a")); - assertEquals("b_a11b", mAms.getUserData(a11, "b")); - assertEquals("c_a11", mAms.getUserData(a11, "c")); - assertEquals("a_a12", mAms.getUserData(a12, "a")); - assertEquals("b_a12", mAms.getUserData(a12, "b")); - assertNull(mAms.getUserData(a12, "c")); - } - - public void testAuthtokens() throws Exception { - Account a11 = new Account("account1", "type1"); - Account a12 = new Account("account1", "type2"); - mAms.addAccount(a11, "p11", null); - mAms.addAccount(a12, "p12", null); - - mAms.setAuthToken(a11, "att1", "a11_att1"); - mAms.setAuthToken(a11, "att2", "a11_att2"); - mAms.setAuthToken(a11, "att3", "a11_att3"); - mAms.setAuthToken(a12, "att1", "a12_att1"); - mAms.setAuthToken(a12, "att2", "a12_att2"); - mAms.setAuthToken(a12, "att3", "a12_att3"); - - assertEquals("a11_att1", mAms.peekAuthToken(a11, "att1")); - assertEquals("a11_att2", mAms.peekAuthToken(a11, "att2")); - assertEquals("a11_att3", mAms.peekAuthToken(a11, "att3")); - assertEquals("a12_att1", mAms.peekAuthToken(a12, "att1")); - assertEquals("a12_att2", mAms.peekAuthToken(a12, "att2")); - assertEquals("a12_att3", mAms.peekAuthToken(a12, "att3")); - - mAms.setAuthToken(a11, "att3", "a11_att3b"); - mAms.invalidateAuthToken(a12.type, "a12_att2"); - - assertEquals("a11_att1", mAms.peekAuthToken(a11, "att1")); - assertEquals("a11_att2", mAms.peekAuthToken(a11, "att2")); - assertEquals("a11_att3b", mAms.peekAuthToken(a11, "att3")); - assertEquals("a12_att1", mAms.peekAuthToken(a12, "att1")); - assertNull(mAms.peekAuthToken(a12, "att2")); - assertEquals("a12_att3", mAms.peekAuthToken(a12, "att3")); - - assertNull(mAms.peekAuthToken(a12, "att2")); - } - - static public class MockAccountAuthenticatorCache implements IAccountAuthenticatorCache { - private ArrayList<ServiceInfo<AuthenticatorDescription>> mServices; - - public MockAccountAuthenticatorCache() { - mServices = new ArrayList<ServiceInfo<AuthenticatorDescription>>(); - AuthenticatorDescription d1 = new AuthenticatorDescription("type1", "p1", 0, 0, 0, 0); - AuthenticatorDescription d2 = new AuthenticatorDescription("type2", "p2", 0, 0, 0, 0); - mServices.add(new ServiceInfo<AuthenticatorDescription>(d1, null, 0)); - mServices.add(new ServiceInfo<AuthenticatorDescription>(d2, null, 0)); - } - - @Override - public ServiceInfo<AuthenticatorDescription> getServiceInfo( - AuthenticatorDescription type, int userId) { - for (ServiceInfo<AuthenticatorDescription> service : mServices) { - if (service.type.equals(type)) { - return service; - } - } - return null; - } - - @Override - public Collection<ServiceInfo<AuthenticatorDescription>> getAllServices(int userId) { - return mServices; - } - - @Override - public void dump( - final FileDescriptor fd, final PrintWriter fout, final String[] args, int userId) { - } - - @Override - public void setListener( - final RegisteredServicesCacheListener<AuthenticatorDescription> listener, - final Handler handler) { - } - - @Override - public void invalidateCache(int userId) { - } - } - - static public class MyMockContext extends MockContext { - @Override - public int checkCallingOrSelfPermission(final String permission) { - return PackageManager.PERMISSION_GRANTED; - } - } - - static public class MyMockPackageManager extends MockPackageManager { - @Override - public int checkSignatures(final int uid1, final int uid2) { - return PackageManager.SIGNATURE_MATCH; - } - } - - static public class MyAccountManagerService extends AccountManagerService { - public MyAccountManagerService(Context context, PackageManager packageManager, - IAccountAuthenticatorCache authenticatorCache) { - super(context, packageManager, authenticatorCache); - } - - @Override - protected void installNotification(final int notificationId, final Notification n, UserHandle user) { - } - - @Override - protected void cancelNotification(final int id, UserHandle user) { - } - } -} diff --git a/core/tests/coretests/src/android/content/SyncOperationTest.java b/core/tests/coretests/src/android/content/SyncOperationTest.java deleted file mode 100644 index 1fd25d2..0000000 --- a/core/tests/coretests/src/android/content/SyncOperationTest.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content; - -import android.accounts.Account; -import android.os.Bundle; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; - -/** - * You can run those tests with: - * - * adb shell am instrument - * -e debug false - * -w - * -e class android.content.SyncOperationTest com.android.frameworks.coretests/android.test.InstrumentationTestRunner - */ - -public class SyncOperationTest extends AndroidTestCase { - - @SmallTest - public void testToKey() { - Account account1 = new Account("account1", "type1"); - Account account2 = new Account("account2", "type2"); - - Bundle b1 = new Bundle(); - Bundle b2 = new Bundle(); - b2.putBoolean("b2", true); - - SyncOperation op1 = new SyncOperation(account1, 0, - 1, - SyncOperation.REASON_PERIODIC, - "authority1", - b1, - 100, - 1000, - 10000, - false); - - // Same as op1 but different time infos - SyncOperation op2 = new SyncOperation(account1, 0, - 1, - SyncOperation.REASON_PERIODIC, - "authority1", - b1, - 200, - 2000, - 20000, - false); - - // Same as op1 but different authority - SyncOperation op3 = new SyncOperation(account1, 0, - 1, - SyncOperation.REASON_PERIODIC, - "authority2", - b1, - 100, - 1000, - 10000, - false); - - // Same as op1 but different account - SyncOperation op4 = new SyncOperation(account2, 0, - 1, - SyncOperation.REASON_PERIODIC, - "authority1", - b1, - 100, - 1000, - 10000, - false); - - // Same as op1 but different bundle - SyncOperation op5 = new SyncOperation(account1, 0, - 1, - SyncOperation.REASON_PERIODIC, - "authority1", - b2, - 100, - 1000, - 10000, - false); - - assertEquals(op1.key, op2.key); - assertNotSame(op1.key, op3.key); - assertNotSame(op1.key, op4.key); - assertNotSame(op1.key, op5.key); - } -} |