diff options
55 files changed, 2250 insertions, 1413 deletions
diff --git a/api/current.txt b/api/current.txt index f79df94158b0..77c9f6ebdcfe 100644 --- a/api/current.txt +++ b/api/current.txt @@ -10786,8 +10786,8 @@ package android.media { public class MediaActionSound { ctor public MediaActionSound(); - method public void load(int); - method public void play(int); + method public synchronized void load(int); + method public synchronized void play(int); method public void release(); field public static final int FOCUS_COMPLETE = 1; // 0x1 field public static final int SHUTTER_CLICK = 0; // 0x0 @@ -23374,6 +23374,7 @@ package android.view { method public void onResolvedTextDirectionReset(); method protected void onRestoreInstanceState(android.os.Parcelable); method protected android.os.Parcelable onSaveInstanceState(); + method public void onScreenStateChanged(int); method protected void onScrollChanged(int, int, int, int); method protected boolean onSetAlpha(int); method protected void onSizeChanged(int, int, int, int); @@ -23583,6 +23584,8 @@ package android.view { field public static final android.util.Property ROTATION_Y; field public static final android.util.Property SCALE_X; field public static final android.util.Property SCALE_Y; + field public static final int SCREEN_STATE_OFF = 0; // 0x0 + field public static final int SCREEN_STATE_ON = 1; // 0x1 field public static final int SCROLLBARS_INSIDE_INSET = 16777216; // 0x1000000 field public static final int SCROLLBARS_INSIDE_OVERLAY = 0; // 0x0 field public static final int SCROLLBARS_OUTSIDE_INSET = 50331648; // 0x3000000 @@ -27515,14 +27518,24 @@ package android.widget { ctor public Switch(android.content.Context); ctor public Switch(android.content.Context, android.util.AttributeSet); ctor public Switch(android.content.Context, android.util.AttributeSet, int); + method public int getSwitchMinWidth(); + method public int getSwitchPadding(); method public java.lang.CharSequence getTextOff(); method public java.lang.CharSequence getTextOn(); + method public android.graphics.drawable.Drawable getThumbDrawable(); + method public int getThumbTextPadding(); + method public android.graphics.drawable.Drawable getTrackDrawable(); method public void onMeasure(int, int); + method public void setSwitchMinWidth(int); + method public void setSwitchPadding(int); method public void setSwitchTextAppearance(android.content.Context, int); method public void setSwitchTypeface(android.graphics.Typeface, int); method public void setSwitchTypeface(android.graphics.Typeface); method public void setTextOff(java.lang.CharSequence); method public void setTextOn(java.lang.CharSequence); + method public void setThumbDrawable(android.graphics.drawable.Drawable); + method public void setThumbTextPadding(int); + method public void setTrackDrawable(android.graphics.drawable.Drawable); } public class TabHost extends android.widget.FrameLayout implements android.view.ViewTreeObserver.OnTouchModeChangeListener { diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java index 5fee4de75920..adc7d3572002 100644 --- a/core/java/android/accounts/AccountManagerService.java +++ b/core/java/android/accounts/AccountManagerService.java @@ -18,6 +18,7 @@ package android.accounts; import android.Manifest; import android.app.ActivityManager; +import android.app.AppGlobals; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -33,6 +34,7 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.RegisteredServicesCache; import android.content.pm.RegisteredServicesCacheListener; +import android.content.pm.UserInfo; import android.content.res.Resources; import android.database.Cursor; import android.database.DatabaseUtils; @@ -48,11 +50,14 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.SystemClock; +import android.os.UserId; import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import android.util.SparseArray; import com.android.internal.R; +import com.android.internal.util.IndentingPrintWriter; import java.io.File; import java.io.FileDescriptor; @@ -62,6 +67,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -94,7 +100,6 @@ public class AccountManagerService private static final int MESSAGE_TIMED_OUT = 3; private final IAccountAuthenticatorCache mAuthenticatorCache; - private final DatabaseHelper mOpenHelper; private static final String TABLE_ACCOUNTS = "accounts"; private static final String ACCOUNTS_ID = "_id"; @@ -148,14 +153,36 @@ public class AccountManagerService private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>(); private final AtomicInteger mNotificationIds = new AtomicInteger(1); - private final HashMap<Pair<Pair<Account, String>, Integer>, Integer> - mCredentialsPermissionNotificationIds = - new HashMap<Pair<Pair<Account, String>, Integer>, Integer>(); - private final HashMap<Account, Integer> mSigninRequiredNotificationIds = - new HashMap<Account, Integer>(); + 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 HashMap<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 { @@ -163,15 +190,6 @@ public class AccountManagerService ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); } - private final Object mCacheLock = new Object(); - /** protected by the {@link #mCacheLock} */ - private final HashMap<String, Account[]> mAccountCache = new HashMap<String, Account[]>(); - /** protected by the {@link #mCacheLock} */ - private HashMap<Account, HashMap<String, String>> mUserDataCache = - new HashMap<Account, HashMap<String, String>>(); - /** protected by the {@link #mCacheLock} */ - private HashMap<Account, HashMap<String, String>> mAuthTokenCache = - new HashMap<Account, HashMap<String, String>>(); /** * This should only be called by system code. One should only call this after the service @@ -192,10 +210,6 @@ public class AccountManagerService mContext = context; mPackageManager = packageManager; - synchronized (mCacheLock) { - mOpenHelper = new DatabaseHelper(mContext); - } - mMessageThread = new HandlerThread("AccountManagerService"); mMessageThread.start(); mMessageHandler = new MessageHandler(mMessageThread.getLooper()); @@ -203,6 +217,8 @@ public class AccountManagerService mAuthenticatorCache = authenticatorCache; mAuthenticatorCache.setListener(this, null /* Handler */); + UserAccounts accounts = initUser(0); + sThis.set(this); IntentFilter intentFilter = new IntentFilter(); @@ -211,17 +227,36 @@ public class AccountManagerService mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context1, Intent intent) { - purgeOldGrants(); + purgeOldGrantsAll(); } }, intentFilter); - purgeOldGrants(); - validateAccountsAndPopulateCache(); } - private void purgeOldGrants() { - synchronized (mCacheLock) { - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + 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); + validateAccountsAndPopulateCache(accounts); + } + 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); @@ -243,15 +278,15 @@ public class AccountManagerService } } - private void validateAccountsAndPopulateCache() { - synchronized (mCacheLock) { - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + private void validateAccountsAndPopulateCache(UserAccounts accounts) { + 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 { - mAccountCache.clear(); + accounts.accountCache.clear(); final HashMap<String, ArrayList<String>> accountNamesByType = new HashMap<String, ArrayList<String>>(); while (cursor.moveToNext()) { @@ -265,8 +300,8 @@ public class AccountManagerService db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null); accountDeleted = true; final Account account = new Account(accountName, accountType); - mUserDataCache.remove(account); - mAuthTokenCache.remove(account); + accounts.userDataCache.remove(account); + accounts.authTokenCache.remove(account); } else { ArrayList<String> accountNames = accountNamesByType.get(accountType); if (accountNames == null) { @@ -286,19 +321,51 @@ public class AccountManagerService accountsForType[i] = new Account(accountName, accountType); ++i; } - mAccountCache.put(accountType, accountsForType); + accounts.accountCache.put(accountType, accountsForType); } } finally { cursor.close(); if (accountDeleted) { - sendAccountsChangedBroadcast(); + sendAccountsChangedBroadcast(accounts.userId); } } } } + private UserAccounts getUserAccountsForCaller() { + return getUserAccounts(UserId.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 List<UserInfo> getAllUsers() { + try { + return AppGlobals.getPackageManager().getUsers(); + } catch (RemoteException re) { + // Local to system process, shouldn't happen + } + return null; + } + public void onServiceChanged(AuthenticatorDescription desc, boolean removed) { - validateAccountsAndPopulateCache(); + // Validate accounts for all users + List<UserInfo> users = getAllUsers(); + if (users == null) { + validateAccountsAndPopulateCache(getUserAccountsForCaller()); + } else { + for (UserInfo user : users) { + validateAccountsAndPopulateCache(getUserAccounts(user.id)); + } + } } public String getPassword(Account account) { @@ -310,21 +377,22 @@ public class AccountManagerService if (account == null) throw new IllegalArgumentException("account is null"); checkAuthenticateAccountsPermission(account); + UserAccounts accounts = getUserAccountsForCaller(); long identityToken = clearCallingIdentity(); try { - return readPasswordInternal(account); + return readPasswordInternal(accounts, account); } finally { restoreCallingIdentity(identityToken); } } - private String readPasswordInternal(Account account) { + private String readPasswordInternal(UserAccounts accounts, Account account) { if (account == null) { return null; } - synchronized (mCacheLock) { - final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + 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); @@ -349,9 +417,10 @@ public class AccountManagerService 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(account, key); + return readUserDataInternal(accounts, account, key); } finally { restoreCallingIdentity(identityToken); } @@ -390,21 +459,23 @@ public class AccountManagerService 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(account, password, extras); + return addAccountInternal(accounts, account, password, extras); } finally { restoreCallingIdentity(identityToken); } } - private boolean addAccountInternal(Account account, String password, Bundle extras) { + private boolean addAccountInternal(UserAccounts accounts, Account account, String password, + Bundle extras) { if (account == null) { return false; } - synchronized (mCacheLock) { - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + synchronized (accounts.cacheLock) { + final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); db.beginTransaction(); try { long numMatches = DatabaseUtils.longForQuery(db, @@ -437,11 +508,11 @@ public class AccountManagerService } } db.setTransactionSuccessful(); - insertAccountIntoCacheLocked(account); + insertAccountIntoCacheLocked(accounts, account); } finally { db.endTransaction(); } - sendAccountsChangedBroadcast(); + sendAccountsChangedBroadcast(accounts.userId); return true; } } @@ -467,9 +538,10 @@ public class AccountManagerService 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(response, account, features).bind(); + new TestFeaturesSession(accounts, response, account, features).bind(); } finally { restoreCallingIdentity(identityToken); } @@ -479,9 +551,9 @@ public class AccountManagerService private final String[] mFeatures; private final Account mAccount; - public TestFeaturesSession(IAccountManagerResponse response, + public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response, Account account, String[] features) { - super(response, account.type, false /* expectActivityLaunch */, + super(accounts, response, account.type, false /* expectActivityLaunch */, true /* stripAuthTokenFromResult */); mFeatures = features; mAccount = account; @@ -537,21 +609,22 @@ public class AccountManagerService if (response == null) throw new IllegalArgumentException("response is null"); if (account == null) throw new IllegalArgumentException("account is null"); checkManageAccountsPermission(); + UserAccounts accounts = getUserAccountsForCaller(); long identityToken = clearCallingIdentity(); - cancelNotification(getSigninRequiredNotificationId(account)); - synchronized(mCredentialsPermissionNotificationIds) { + cancelNotification(getSigninRequiredNotificationId(accounts, account)); + synchronized(accounts.credentialsPermissionNotificationIds) { for (Pair<Pair<Account, String>, Integer> pair: - mCredentialsPermissionNotificationIds.keySet()) { + accounts.credentialsPermissionNotificationIds.keySet()) { if (account.equals(pair.first.first)) { - int id = mCredentialsPermissionNotificationIds.get(pair); + int id = accounts.credentialsPermissionNotificationIds.get(pair); cancelNotification(id); } } } try { - new RemoveAccountSession(response, account).bind(); + new RemoveAccountSession(accounts, response, account).bind(); } finally { restoreCallingIdentity(identityToken); } @@ -559,8 +632,9 @@ public class AccountManagerService private class RemoveAccountSession extends Session { final Account mAccount; - public RemoveAccountSession(IAccountManagerResponse response, Account account) { - super(response, account.type, false /* expectActivityLaunch */, + public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response, + Account account) { + super(accounts, response, account.type, false /* expectActivityLaunch */, true /* stripAuthTokenFromResult */); mAccount = account; } @@ -579,7 +653,7 @@ public class AccountManagerService && !result.containsKey(AccountManager.KEY_INTENT)) { final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT); if (removalAllowed) { - removeAccountInternal(mAccount); + removeAccountInternal(mAccounts, mAccount); } IAccountManagerResponse response = getResponseAndClose(); if (response != null) { @@ -600,13 +674,18 @@ public class AccountManagerService } } + /* For testing */ protected void removeAccountInternal(Account account) { - synchronized (mCacheLock) { - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + 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(account); - sendAccountsChangedBroadcast(); + removeAccountFromCacheLocked(accounts, account); + sendAccountsChangedBroadcast(accounts.userId); } } @@ -619,13 +698,14 @@ public class AccountManagerService 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 (mCacheLock) { - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + synchronized (accounts.cacheLock) { + final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); db.beginTransaction(); try { - invalidateAuthTokenLocked(db, accountType, authToken); + invalidateAuthTokenLocked(accounts, db, accountType, authToken); db.setTransactionSuccessful(); } finally { db.endTransaction(); @@ -636,7 +716,8 @@ public class AccountManagerService } } - private void invalidateAuthTokenLocked(SQLiteDatabase db, String accountType, String authToken) { + private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db, + String accountType, String authToken) { if (authToken == null || accountType == null) { return; } @@ -657,7 +738,7 @@ public class AccountManagerService String accountName = cursor.getString(1); String authTokenType = cursor.getString(2); db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null); - writeAuthTokenIntoCacheLocked(db, new Account(accountName, accountType), + writeAuthTokenIntoCacheLocked(accounts, db, new Account(accountName, accountType), authTokenType, null); } } finally { @@ -665,13 +746,14 @@ public class AccountManagerService } } - private boolean saveAuthTokenToDatabase(Account account, String type, String authToken) { + private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type, + String authToken) { if (account == null || type == null) { return false; } - cancelNotification(getSigninRequiredNotificationId(account)); - synchronized (mCacheLock) { - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + cancelNotification(getSigninRequiredNotificationId(accounts, account)); + synchronized (accounts.cacheLock) { + final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); db.beginTransaction(); try { long accountId = getAccountIdLocked(db, account); @@ -687,7 +769,7 @@ public class AccountManagerService values.put(AUTHTOKENS_AUTHTOKEN, authToken); if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) { db.setTransactionSuccessful(); - writeAuthTokenIntoCacheLocked(db, account, type, authToken); + writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken); return true; } return false; @@ -707,9 +789,10 @@ public class AccountManagerService 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(account, authTokenType); + return readAuthTokenInternal(accounts, account, authTokenType); } finally { restoreCallingIdentity(identityToken); } @@ -725,9 +808,10 @@ public class AccountManagerService 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(account, authTokenType, authToken); + saveAuthTokenToDatabase(accounts, account, authTokenType, authToken); } finally { restoreCallingIdentity(identityToken); } @@ -741,20 +825,21 @@ public class AccountManagerService } if (account == null) throw new IllegalArgumentException("account is null"); checkAuthenticateAccountsPermission(account); + UserAccounts accounts = getUserAccountsForCaller(); long identityToken = clearCallingIdentity(); try { - setPasswordInternal(account, password); + setPasswordInternal(accounts, account, password); } finally { restoreCallingIdentity(identityToken); } } - private void setPasswordInternal(Account account, String password) { + private void setPasswordInternal(UserAccounts accounts, Account account, String password) { if (account == null) { return; } - synchronized (mCacheLock) { - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + synchronized (accounts.cacheLock) { + final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); db.beginTransaction(); try { final ContentValues values = new ContentValues(); @@ -764,20 +849,20 @@ public class AccountManagerService final String[] argsAccountId = {String.valueOf(accountId)}; db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId); db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId); - mAuthTokenCache.remove(account); + accounts.authTokenCache.remove(account); db.setTransactionSuccessful(); } } finally { db.endTransaction(); } - sendAccountsChangedBroadcast(); + sendAccountsChangedBroadcast(accounts.userId); } } - private void sendAccountsChangedBroadcast() { + private void sendAccountsChangedBroadcast(int userId) { Log.i(TAG, "the accounts changed, sending broadcast of " + ACCOUNTS_CHANGED_INTENT.getAction()); - mContext.sendBroadcast(ACCOUNTS_CHANGED_INTENT); + mContext.sendBroadcast(ACCOUNTS_CHANGED_INTENT, userId); } public void clearPassword(Account account) { @@ -788,9 +873,10 @@ public class AccountManagerService } if (account == null) throw new IllegalArgumentException("account is null"); checkManageAccountsPermission(); + UserAccounts accounts = getUserAccountsForCaller(); long identityToken = clearCallingIdentity(); try { - setPasswordInternal(account, null); + setPasswordInternal(accounts, account, null); } finally { restoreCallingIdentity(identityToken); } @@ -806,20 +892,22 @@ public class AccountManagerService 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(account, key, value); + setUserdataInternal(accounts, account, key, value); } finally { restoreCallingIdentity(identityToken); } } - private void setUserdataInternal(Account account, String key, String value) { + private void setUserdataInternal(UserAccounts accounts, Account account, String key, + String value) { if (account == null || key == null) { return; } - synchronized (mCacheLock) { - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + synchronized (accounts.cacheLock) { + final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); db.beginTransaction(); try { long accountId = getAccountIdLocked(db, account); @@ -840,7 +928,7 @@ public class AccountManagerService } } - writeUserDataIntoCacheLocked(db, account, key, value); + writeUserDataIntoCacheLocked(accounts, db, account, key, value); db.setTransactionSuccessful(); } finally { db.endTransaction(); @@ -868,15 +956,16 @@ public class AccountManagerService } void getAuthTokenLabel(final IAccountManagerResponse response, - final Account account, final String authTokenType) { + final Account account, + final String authTokenType, int uid) { if (account == null) throw new IllegalArgumentException("account is null"); if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); checkBinderPermission(Manifest.permission.USE_CREDENTIALS); - + UserAccounts accounts = getUserAccounts(UserId.getUserId(uid)); long identityToken = clearCallingIdentity(); try { - new Session(response, account.type, false, + new Session(accounts, response, account.type, false, false /* stripAuthTokenFromResult */) { protected String toDebugString(long now) { return super.toDebugString(now) + ", getAuthTokenLabel" @@ -921,6 +1010,7 @@ public class AccountManagerService if (account == null) throw new IllegalArgumentException("account is null"); if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); checkBinderPermission(Manifest.permission.USE_CREDENTIALS); + UserAccounts accounts = getUserAccountsForCaller(); AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo = mAuthenticatorCache.getServiceInfo( AuthenticatorDescription.newKey(account.type)); @@ -946,7 +1036,7 @@ public class AccountManagerService // if the caller has permission, do the peek. otherwise go the more expensive // route of starting a Session if (!customTokens && permissionGranted) { - String authToken = readAuthTokenInternal(account, authTokenType); + String authToken = readAuthTokenInternal(accounts, account, authTokenType); if (authToken != null) { Bundle result = new Bundle(); result.putString(AccountManager.KEY_AUTHTOKEN, authToken); @@ -957,7 +1047,7 @@ public class AccountManagerService } } - new Session(response, account.type, expectActivityLaunch, + new Session(accounts, response, account.type, expectActivityLaunch, false /* stripAuthTokenFromResult */) { protected String toDebugString(long now) { if (loginOptions != null) loginOptions.keySet(); @@ -1000,14 +1090,14 @@ public class AccountManagerService return; } if (!customTokens) { - saveAuthTokenToDatabase(new Account(name, type), + saveAuthTokenToDatabase(mAccounts, new Account(name, type), authTokenType, authToken); } } Intent intent = result.getParcelable(AccountManager.KEY_INTENT); if (intent != null && notifyOnAuthFailure && !customTokens) { - doNotification( + doNotification(mAccounts, account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE), intent); } @@ -1090,26 +1180,27 @@ public class AccountManagerService private Integer getCredentialPermissionNotificationId(Account account, String authTokenType, int uid) { Integer id; - synchronized(mCredentialsPermissionNotificationIds) { + UserAccounts accounts = getUserAccounts(UserId.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 = mCredentialsPermissionNotificationIds.get(key); + id = accounts.credentialsPermissionNotificationIds.get(key); if (id == null) { id = mNotificationIds.incrementAndGet(); - mCredentialsPermissionNotificationIds.put(key, id); + accounts.credentialsPermissionNotificationIds.put(key, id); } } return id; } - private Integer getSigninRequiredNotificationId(Account account) { + private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) { Integer id; - synchronized(mSigninRequiredNotificationIds) { - id = mSigninRequiredNotificationIds.get(account); + synchronized (accounts.signinRequiredNotificationIds) { + id = accounts.signinRequiredNotificationIds.get(account); if (id == null) { id = mNotificationIds.incrementAndGet(); - mSigninRequiredNotificationIds.put(account, id); + accounts.signinRequiredNotificationIds.put(account, id); } } return id; @@ -1131,6 +1222,7 @@ public class AccountManagerService 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; @@ -1139,7 +1231,7 @@ public class AccountManagerService long identityToken = clearCallingIdentity(); try { - new Session(response, accountType, expectActivityLaunch, + new Session(accounts, response, accountType, expectActivityLaunch, true /* stripAuthTokenFromResult */) { public void run() throws RemoteException { mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures, @@ -1172,9 +1264,10 @@ public class AccountManagerService if (response == null) throw new IllegalArgumentException("response is null"); if (account == null) throw new IllegalArgumentException("account is null"); checkManageAccountsPermission(); + UserAccounts accounts = getUserAccountsForCaller(); long identityToken = clearCallingIdentity(); try { - new Session(response, account.type, expectActivityLaunch, + new Session(accounts, response, account.type, expectActivityLaunch, true /* stripAuthTokenFromResult */) { public void run() throws RemoteException { mAuthenticator.confirmCredentials(this, account, options); @@ -1204,9 +1297,10 @@ public class AccountManagerService 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(response, account.type, expectActivityLaunch, + new Session(accounts, response, account.type, expectActivityLaunch, true /* stripAuthTokenFromResult */) { public void run() throws RemoteException { mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions); @@ -1236,9 +1330,10 @@ public class AccountManagerService 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(response, accountType, expectActivityLaunch, + new Session(accounts, response, accountType, expectActivityLaunch, true /* stripAuthTokenFromResult */) { public void run() throws RemoteException { mAuthenticator.editProperties(this, mAccountType); @@ -1259,16 +1354,16 @@ public class AccountManagerService private volatile ArrayList<Account> mAccountsWithFeatures = null; private volatile int mCurrentAccount = 0; - public GetAccountsByTypeAndFeatureSession(IAccountManagerResponse response, - String type, String[] features) { - super(response, type, false /* expectActivityLaunch */, + 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 (mCacheLock) { - mAccountsOfType = getAccountsFromCacheLocked(mAccountType); + synchronized (mAccounts.cacheLock) { + mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType); } // check whether each account matches the requested features mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length); @@ -1346,6 +1441,23 @@ public class AccountManagerService } } + /** + * 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); + } + } + public Account[] getAccounts(String type) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "getAccounts: accountType " + type @@ -1353,10 +1465,11 @@ public class AccountManagerService + ", pid " + Binder.getCallingPid()); } checkReadAccountsPermission(); + UserAccounts accounts = getUserAccountsForCaller(); long identityToken = clearCallingIdentity(); try { - synchronized (mCacheLock) { - return getAccountsFromCacheLocked(type); + synchronized (accounts.cacheLock) { + return getAccountsFromCacheLocked(accounts, type); } } finally { restoreCallingIdentity(identityToken); @@ -1375,19 +1488,20 @@ public class AccountManagerService 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 (mCacheLock) { - accounts = getAccountsFromCacheLocked(type); + synchronized (userAccounts.cacheLock) { + accounts = getAccountsFromCacheLocked(userAccounts, type); } Bundle result = new Bundle(); result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); onResult(response, result); return; } - new GetAccountsByTypeAndFeatureSession(response, type, features).bind(); + new GetAccountsByTypeAndFeatureSession(userAccounts, response, type, features).bind(); } finally { restoreCallingIdentity(identityToken); } @@ -1435,12 +1549,14 @@ public class AccountManagerService IAccountAuthenticator mAuthenticator = null; private final boolean mStripAuthTokenFromResult; + protected final UserAccounts mAccounts; - public Session(IAccountManagerResponse response, String accountType, + 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; @@ -1578,7 +1694,7 @@ public class AccountManagerService String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE); if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) { Account account = new Account(accountName, accountType); - cancelNotification(getSigninRequiredNotificationId(account)); + cancelNotification(getSigninRequiredNotificationId(mAccounts, account)); } } IAccountManagerResponse response; @@ -1694,20 +1810,23 @@ public class AccountManagerService } } - private static String getDatabaseName() { - if(Environment.isEncryptedFilesystemEnabled()) { - // Hard-coded path in case of encrypted file system - return Environment.getSystemSecureDirectory().getPath() + File.separator + DATABASE_NAME; - } else { - // Regular path in case of non-encrypted file system - return DATABASE_NAME; + private static String getDatabaseName(int userId) { + File systemDir = Environment.getSystemSecureDirectory(); + File databaseFile = new File(systemDir, "users/" + userId + "/" + DATABASE_NAME); + if (userId == 0) { + // Migrate old file, if it exists, to the new location + File oldFile = new File(systemDir, DATABASE_NAME); + if (oldFile.exists()) { + oldFile.renameTo(databaseFile); + } } + return databaseFile.getPath(); } - private class DatabaseHelper extends SQLiteOpenHelper { + static class DatabaseHelper extends SQLiteOpenHelper { - public DatabaseHelper(Context context) { - super(context, AccountManagerService.getDatabaseName(), null, DATABASE_VERSION); + public DatabaseHelper(Context context, int userId) { + super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION); } /** @@ -1799,15 +1918,6 @@ public class AccountManagerService } } - private void setMetaValue(String key, String value) { - ContentValues values = new ContentValues(); - values.put(META_KEY, key); - values.put(META_VALUE, value); - synchronized (mCacheLock) { - mOpenHelper.getWritableDatabase().replace(TABLE_META, META_KEY, values); - } - } - public IBinder onBind(Intent intent) { return asBinder(); } @@ -1837,11 +1947,25 @@ public class AccountManagerService + " without permission " + android.Manifest.permission.DUMP); return; } + final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c"); - synchronized (mCacheLock) { - final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + fout = new IndentingPrintWriter(fout, " "); + int size = mUsers.size(); + for (int i = 0; i < size; i++) { + fout.println("User " + mUsers.keyAt(i) + ":"); + ((IndentingPrintWriter) fout).increaseIndent(); + dumpUser(mUsers.valueAt(i), fd, fout, args, isCheckinRequest); + ((IndentingPrintWriter) fout).decreaseIndent(); + if (i < size - 1) { + fout.println(); + } + } + } - final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c"); + 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. @@ -1858,7 +1982,7 @@ public class AccountManagerService } } } else { - Account[] accounts = getAccountsFromCacheLocked(null /* type */); + Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */); fout.println("Accounts: " + accounts.length); for (Account account : accounts) { fout.println(" " + account); @@ -1879,7 +2003,8 @@ public class AccountManagerService } } - private void doNotification(Account account, CharSequence message, Intent intent) { + private void doNotification(UserAccounts accounts, Account account, CharSequence message, + Intent intent) { long identityToken = clearCallingIdentity(); try { if (Log.isLoggable(TAG, Log.VERBOSE)) { @@ -1891,7 +2016,7 @@ public class AccountManagerService intent.getComponent().getClassName())) { createNoCredentialsPermissionNotification(account, intent); } else { - final Integer notificationId = getSigninRequiredNotificationId(account); + final Integer notificationId = getSigninRequiredNotificationId(accounts, account); intent.addCategory(String.valueOf(notificationId)); Notification n = new Notification(android.R.drawable.stat_sys_warning, null, 0 /* when */); @@ -1962,7 +2087,7 @@ public class AccountManagerService final boolean fromAuthenticator = account != null && hasAuthenticatorUid(account.type, callerUid); final boolean hasExplicitGrants = account != null - && hasExplicitlyGrantedPermission(account, authTokenType); + && hasExplicitlyGrantedPermission(account, authTokenType, callerUid); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid " + callerUid + ", " + account @@ -1984,13 +2109,15 @@ public class AccountManagerService return false; } - private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType) { - if (Binder.getCallingUid() == android.os.Process.SYSTEM_UID) { + private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType, + int callerUid) { + if (callerUid == android.os.Process.SYSTEM_UID) { return true; } - synchronized (mCacheLock) { - final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); - String[] args = {String.valueOf(Binder.getCallingUid()), authTokenType, + 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; @@ -1998,7 +2125,7 @@ public class AccountManagerService // 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 " + Binder.getCallingUid() + + authTokenType + " by uid " + callerUid + " but ignoring since device is in test harness."); return true; } @@ -2048,8 +2175,9 @@ public class AccountManagerService Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception()); return; } - synchronized (mCacheLock) { - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + UserAccounts accounts = getUserAccounts(UserId.getUserId(uid)); + synchronized (accounts.cacheLock) { + final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); db.beginTransaction(); try { long accountId = getAccountIdLocked(db, account); @@ -2081,8 +2209,9 @@ public class AccountManagerService Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception()); return; } - synchronized (mCacheLock) { - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + UserAccounts accounts = getUserAccounts(UserId.getUserId(uid)); + synchronized (accounts.cacheLock) { + final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); db.beginTransaction(); try { long accountId = getAccountIdLocked(db, account); @@ -2105,8 +2234,8 @@ public class AccountManagerService return value != null ? ("[" + TextUtils.join(",", value) + "]") : null; } - private void removeAccountFromCacheLocked(Account account) { - final Account[] oldAccountsForType = mAccountCache.get(account.type); + 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) { @@ -2115,34 +2244,34 @@ public class AccountManagerService } } if (newAccountsList.isEmpty()) { - mAccountCache.remove(account.type); + accounts.accountCache.remove(account.type); } else { Account[] newAccountsForType = new Account[newAccountsList.size()]; newAccountsForType = newAccountsList.toArray(newAccountsForType); - mAccountCache.put(account.type, newAccountsForType); + accounts.accountCache.put(account.type, newAccountsForType); } } - mUserDataCache.remove(account); - mAuthTokenCache.remove(account); + 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(Account account) { - Account[] accountsForType = mAccountCache.get(account.type); + 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; - mAccountCache.put(account.type, newAccountsForType); + accounts.accountCache.put(account.type, newAccountsForType); } - protected Account[] getAccountsFromCacheLocked(String accountType) { + protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType) { if (accountType != null) { - final Account[] accounts = mAccountCache.get(accountType); + final Account[] accounts = userAccounts.accountCache.get(accountType); if (accounts == null) { return EMPTY_ACCOUNT_ARRAY; } else { @@ -2150,7 +2279,7 @@ public class AccountManagerService } } else { int totalLength = 0; - for (Account[] accounts : mAccountCache.values()) { + for (Account[] accounts : userAccounts.accountCache.values()) { totalLength += accounts.length; } if (totalLength == 0) { @@ -2158,7 +2287,7 @@ public class AccountManagerService } Account[] accounts = new Account[totalLength]; totalLength = 0; - for (Account[] accountsOfType : mAccountCache.values()) { + for (Account[] accountsOfType : userAccounts.accountCache.values()) { System.arraycopy(accountsOfType, 0, accounts, totalLength, accountsOfType.length); totalLength += accountsOfType.length; @@ -2167,12 +2296,12 @@ public class AccountManagerService } } - protected void writeUserDataIntoCacheLocked(final SQLiteDatabase db, Account account, - String key, String value) { - HashMap<String, String> userDataForAccount = mUserDataCache.get(account); + 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); - mUserDataCache.put(account, userDataForAccount); + accounts.userDataCache.put(account, userDataForAccount); } if (value == null) { userDataForAccount.remove(key); @@ -2181,12 +2310,12 @@ public class AccountManagerService } } - protected void writeAuthTokenIntoCacheLocked(final SQLiteDatabase db, Account account, - String key, String value) { - HashMap<String, String> authTokensForAccount = mAuthTokenCache.get(account); + 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); - mAuthTokenCache.put(account, authTokensForAccount); + accounts.authTokenCache.put(account, authTokensForAccount); } if (value == null) { authTokensForAccount.remove(key); @@ -2195,27 +2324,28 @@ public class AccountManagerService } } - protected String readAuthTokenInternal(Account account, String authTokenType) { - synchronized (mCacheLock) { - HashMap<String, String> authTokensForAccount = mAuthTokenCache.get(account); + 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 = mOpenHelper.getReadableDatabase(); + final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account); - mAuthTokenCache.put(account, authTokensForAccount); + accounts.authTokenCache.put(account, authTokensForAccount); } return authTokensForAccount.get(authTokenType); } } - protected String readUserDataInternal(Account account, String key) { - synchronized (mCacheLock) { - HashMap<String, String> userDataForAccount = mUserDataCache.get(account); + 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 = mOpenHelper.getReadableDatabase(); + final SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account); - mUserDataCache.put(account, userDataForAccount); + accounts.userDataCache.put(account, userDataForAccount); } return userDataForAccount.get(key); } diff --git a/core/java/android/accounts/GrantCredentialsPermissionActivity.java b/core/java/android/accounts/GrantCredentialsPermissionActivity.java index 0ee683cb7ee5..4419c8c21fb0 100644 --- a/core/java/android/accounts/GrantCredentialsPermissionActivity.java +++ b/core/java/android/accounts/GrantCredentialsPermissionActivity.java @@ -113,8 +113,7 @@ public class GrantCredentialsPermissionActivity extends Activity implements View } }; - accountManagerService.getAuthTokenLabel( - response, mAccount, mAuthTokenType); + accountManagerService.getAuthTokenLabel(response, mAccount, mAuthTokenType, mUid); findViewById(R.id.allow_button).setOnClickListener(this); findViewById(R.id.deny_button).setOnClickListener(this); diff --git a/core/java/android/accounts/OnAccountsUpdateListener.java b/core/java/android/accounts/OnAccountsUpdateListener.java index 38b371d26e4f..2b4ee50114bd 100644 --- a/core/java/android/accounts/OnAccountsUpdateListener.java +++ b/core/java/android/accounts/OnAccountsUpdateListener.java @@ -17,11 +17,11 @@ package android.accounts; /** - * An interface that contains the callback used by the AccountMonitor + * An interface that contains the callback used by the AccountManager */ public interface OnAccountsUpdateListener { /** - * This invoked when the AccountMonitor starts up and whenever the account + * This invoked when the AccountManager starts up and whenever the account * set changes. * @param accounts the current accounts */ diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index 6fbeee36e03a..954ae66c7f91 100755 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -645,7 +645,7 @@ public class ValueAnimator extends Animator { // onAnimate to process the next frame of the animations. if (!mAnimationScheduled && (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty())) { - mChoreographer.postAnimationCallback(this); + mChoreographer.postAnimationCallback(this, null); mAnimationScheduled = true; } } diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java index fc4c262e8413..f827c3de43c3 100644 --- a/core/java/android/content/ContentService.java +++ b/core/java/android/content/ContentService.java @@ -26,6 +26,7 @@ import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserId; import android.util.Log; import android.util.SparseIntArray; import android.Manifest; @@ -163,6 +164,8 @@ public final class ContentService extends IContentService.Stub { Log.v(TAG, "Notifying update of " + uri + " from observer " + observer + ", syncToNetwork " + syncToNetwork); } + + int userId = UserId.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(); @@ -201,7 +204,8 @@ public final class ContentService extends IContentService.Stub { if (syncToNetwork) { SyncManager syncManager = getSyncManager(); if (syncManager != null) { - syncManager.scheduleLocalSync(null /* all accounts */, uri.getAuthority()); + syncManager.scheduleLocalSync(null /* all accounts */, userId, + uri.getAuthority()); } } } finally { @@ -229,13 +233,15 @@ public final class ContentService extends IContentService.Stub { public void requestSync(Account account, String authority, Bundle extras) { ContentResolver.validateSyncExtrasBundle(extras); + int userId = UserId.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.scheduleSync(account, authority, extras, 0 /* no delay */, + syncManager.scheduleSync(account, userId, authority, extras, 0 /* no delay */, false /* onlyThoseWithUnkownSyncableState */); } } finally { @@ -250,14 +256,16 @@ public final class ContentService extends IContentService.Stub { * @param authority filter the pending and active syncs to cancel using this authority */ public void cancelSync(Account account, String authority) { + int userId = UserId.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, authority); - syncManager.cancelActiveSync(account, authority); + syncManager.clearScheduledSyncOperations(account, userId, authority); + syncManager.cancelActiveSync(account, userId, authority); } } finally { restoreCallingIdentity(identityToken); @@ -283,12 +291,14 @@ public final class ContentService extends IContentService.Stub { public boolean getSyncAutomatically(Account account, String providerName) { mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, "no permission to read the sync settings"); + int userId = UserId.getCallingUserId(); + long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); if (syncManager != null) { return syncManager.getSyncStorageEngine().getSyncAutomatically( - account, providerName); + account, userId, providerName); } } finally { restoreCallingIdentity(identityToken); @@ -299,12 +309,14 @@ public final class ContentService extends IContentService.Stub { 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 = UserId.getCallingUserId(); + long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); if (syncManager != null) { syncManager.getSyncStorageEngine().setSyncAutomatically( - account, providerName, sync); + account, userId, providerName, sync); } } finally { restoreCallingIdentity(identityToken); @@ -315,10 +327,12 @@ public final class ContentService extends IContentService.Stub { long pollFrequency) { mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, "no permission to write the sync settings"); + int userId = UserId.getCallingUserId(); + long identityToken = clearCallingIdentity(); try { getSyncManager().getSyncStorageEngine().addPeriodicSync( - account, authority, extras, pollFrequency); + account, userId, authority, extras, pollFrequency); } finally { restoreCallingIdentity(identityToken); } @@ -327,9 +341,12 @@ public final class ContentService extends IContentService.Stub { 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 = UserId.getCallingUserId(); + long identityToken = clearCallingIdentity(); try { - getSyncManager().getSyncStorageEngine().removePeriodicSync(account, authority, extras); + getSyncManager().getSyncStorageEngine().removePeriodicSync(account, userId, authority, + extras); } finally { restoreCallingIdentity(identityToken); } @@ -338,10 +355,12 @@ public final class ContentService extends IContentService.Stub { public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) { mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, "no permission to read the sync settings"); + int userId = UserId.getCallingUserId(); + long identityToken = clearCallingIdentity(); try { return getSyncManager().getSyncStorageEngine().getPeriodicSyncs( - account, providerName); + account, userId, providerName); } finally { restoreCallingIdentity(identityToken); } @@ -350,12 +369,14 @@ public final class ContentService extends IContentService.Stub { public int getIsSyncable(Account account, String providerName) { mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, "no permission to read the sync settings"); + int userId = UserId.getCallingUserId(); + long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); if (syncManager != null) { return syncManager.getSyncStorageEngine().getIsSyncable( - account, providerName); + account, userId, providerName); } } finally { restoreCallingIdentity(identityToken); @@ -366,12 +387,14 @@ public final class ContentService extends IContentService.Stub { 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 = UserId.getCallingUserId(); + long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); if (syncManager != null) { syncManager.getSyncStorageEngine().setIsSyncable( - account, providerName, syncable); + account, userId, providerName, syncable); } } finally { restoreCallingIdentity(identityToken); @@ -381,11 +404,13 @@ public final class ContentService extends IContentService.Stub { public boolean getMasterSyncAutomatically() { mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, "no permission to read the sync settings"); + int userId = UserId.getCallingUserId(); + long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); if (syncManager != null) { - return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(); + return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId); } } finally { restoreCallingIdentity(identityToken); @@ -396,11 +421,13 @@ public final class ContentService extends IContentService.Stub { public void setMasterSyncAutomatically(boolean flag) { mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, "no permission to write the sync settings"); + int userId = UserId.getCallingUserId(); + long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); if (syncManager != null) { - syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag); + syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId); } } finally { restoreCallingIdentity(identityToken); @@ -410,12 +437,14 @@ public final class ContentService extends IContentService.Stub { public boolean isSyncActive(Account account, String authority) { mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, "no permission to read the sync stats"); + int userId = UserId.getCallingUserId(); + long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); if (syncManager != null) { return syncManager.getSyncStorageEngine().isSyncActive( - account, authority); + account, userId, authority); } } finally { restoreCallingIdentity(identityToken); @@ -426,9 +455,11 @@ public final class ContentService extends IContentService.Stub { public List<SyncInfo> getCurrentSyncs() { mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, "no permission to read the sync stats"); + int userId = UserId.getCallingUserId(); + long identityToken = clearCallingIdentity(); try { - return getSyncManager().getSyncStorageEngine().getCurrentSyncs(); + return getSyncManager().getSyncStorageEngine().getCurrentSyncs(userId); } finally { restoreCallingIdentity(identityToken); } @@ -437,12 +468,14 @@ public final class ContentService extends IContentService.Stub { public SyncStatusInfo getSyncStatus(Account account, String authority) { mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, "no permission to read the sync stats"); + int userId = UserId.getCallingUserId(); + long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); if (syncManager != null) { return syncManager.getSyncStorageEngine().getStatusByAccountAndAuthority( - account, authority); + account, userId, authority); } } finally { restoreCallingIdentity(identityToken); @@ -453,11 +486,13 @@ public final class ContentService extends IContentService.Stub { public boolean isSyncPending(Account account, String authority) { mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, "no permission to read the sync stats"); + int userId = UserId.getCallingUserId(); + long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); if (syncManager != null) { - return syncManager.getSyncStorageEngine().isSyncPending(account, authority); + return syncManager.getSyncStorageEngine().isSyncPending(account, userId, authority); } } finally { restoreCallingIdentity(identityToken); diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index ba24036f0973..b7dfe92f067c 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -23,18 +23,23 @@ import com.google.android.collect.Maps; import android.accounts.Account; import android.accounts.AccountManager; +import android.accounts.AccountManagerService; import android.accounts.OnAccountsUpdateListener; import android.app.ActivityManager; import android.app.AlarmManager; +import android.app.AppGlobals; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.DownloadManager.Request; +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; @@ -48,6 +53,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.UserId; import android.os.WorkSource; import android.provider.Settings; import android.text.format.DateUtils; @@ -132,7 +138,9 @@ public class SyncManager implements OnAccountsUpdateListener { private Context mContext; - private volatile Account[] mAccounts = INITIAL_ACCOUNTS_ARRAY; + private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0]; + + private volatile AccountAndUser[] mAccounts = INITIAL_ACCOUNTS_ARRAY; volatile private PowerManager.WakeLock mHandleAlarmWakeLock; volatile private PowerManager.WakeLock mSyncManagerWakeLock; @@ -166,7 +174,8 @@ public class SyncManager implements OnAccountsUpdateListener { Log.v(TAG, "Internal storage is low."); } mStorageIsLow = true; - cancelActiveSync(null /* any account */, null /* any authority */); + cancelActiveSync(null /* any account */, UserId.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."); @@ -186,28 +195,73 @@ public class SyncManager implements OnAccountsUpdateListener { private BroadcastReceiver mBackgroundDataSettingChanged = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { if (getConnectivityManager().getBackgroundDataSetting()) { - scheduleSync(null /* account */, null /* authority */, new Bundle(), 0 /* delay */, + scheduleSync(null /* account */, UserId.USER_ALL, null /* authority */, + new Bundle(), 0 /* delay */, false /* onlyThoseWithUnknownSyncableState */); } } }; - private static final Account[] INITIAL_ACCOUNTS_ARRAY = new Account[0]; - private final PowerManager mPowerManager; 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() { + try { + return AppGlobals.getPackageManager().getUsers(); + } catch (RemoteException re) { + // Local to system process, shouldn't happen + } + return null; + } + + 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 onAccountsUpdated(Account[] accounts) { // remember if this was the first time this was called after an update final boolean justBootedUp = mAccounts == INITIAL_ACCOUNTS_ARRAY; - mAccounts = accounts; - // if a sync is in progress yet it is no longer in the accounts list, - // cancel it + List<UserInfo> users = getAllUsers(); + if (users == null) return; + + int count = 0; + + // For all known users on the system, get their accounts and add them to the list + // TODO: Limit this to active users, when such a concept exists. + for (UserInfo user : users) { + accounts = AccountManagerService.getSingleton().getAccounts(user.id); + count += accounts.length; + } + + AccountAndUser[] allAccounts = new AccountAndUser[count]; + int index = 0; + for (UserInfo user : users) { + accounts = AccountManagerService.getSingleton().getAccounts(user.id); + for (Account account : accounts) { + allAccounts[index++] = new AccountAndUser(account, user.id); + } + if (mBootCompleted) { + mSyncStorageEngine.doDatabaseCleanup(accounts, user.id); + } + } + + mAccounts = allAccounts; + for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) { - if (!ArrayUtils.contains(accounts, currentSyncContext.mSyncOperation.account)) { + if (!containsAccountAndUser(allAccounts, + currentSyncContext.mSyncOperation.account, + currentSyncContext.mSyncOperation.userId)) { Log.d(TAG, "canceling sync since the account has been removed"); sendSyncFinishedOrCanceledMessage(currentSyncContext, null /* no result since this is a cancel */); @@ -218,11 +272,7 @@ public class SyncManager implements OnAccountsUpdateListener { // the accounts are not set yet sendCheckAlarmsMessage(); - if (mBootCompleted) { - mSyncStorageEngine.doDatabaseCleanup(accounts); - } - - if (accounts.length > 0) { + if (allAccounts.length > 0) { // If this is the first time this was called after a bootup then // the accounts haven't really changed, instead they were just loaded // from the AccountManager. Otherwise at least one of the accounts @@ -238,7 +288,8 @@ public class SyncManager implements OnAccountsUpdateListener { // a chance to set their syncable state. boolean onlyThoseWithUnkownSyncableState = justBootedUp; - scheduleSync(null, null, null, 0 /* no delay */, onlyThoseWithUnkownSyncableState); + scheduleSync(null, UserId.USER_ALL, null, null, 0 /* no delay */, + onlyThoseWithUnkownSyncableState); } } @@ -277,10 +328,36 @@ public class SyncManager implements OnAccountsUpdateListener { private static final String ACTION_SYNC_ALARM = "android.content.syncmanager.SYNC_ALARM"; private final SyncHandler mSyncHandler; - private final Handler mMainHandler; private volatile boolean mBootCompleted = false; + static class AccountAndUser { + Account account; + int userId; + + AccountAndUser(Account account, int userId) { + this.account = account; + this.userId = userId; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof AccountAndUser)) return false; + final AccountAndUser other = (AccountAndUser) o; + return this.account.equals(other.account) + && this.userId == other.userId; + } + + @Override + public int hashCode() { + return account.hashCode() + userId; + } + + public String toString() { + return account.toString() + " u" + userId; + } + } + private ConnectivityManager getConnectivityManager() { synchronized (this) { if (mConnManagerDoNotUseDirectly == null) { @@ -297,6 +374,13 @@ public class SyncManager implements OnAccountsUpdateListener { mContext = context; SyncStorageEngine.init(context); mSyncStorageEngine = SyncStorageEngine.getSingleton(); + mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() { + public void onSyncRequest(Account account, int userId, String authority, + Bundle extras) { + scheduleSync(account, userId, authority, extras, 0, false); + } + }); + mSyncAdapters = new SyncAdaptersCache(mContext); mSyncQueue = new SyncQueue(mSyncStorageEngine, mSyncAdapters); @@ -304,12 +388,11 @@ public class SyncManager implements OnAccountsUpdateListener { Process.THREAD_PRIORITY_BACKGROUND); syncThread.start(); mSyncHandler = new SyncHandler(syncThread.getLooper()); - mMainHandler = new Handler(mContext.getMainLooper()); mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() { public void onServiceChanged(SyncAdapterType type, boolean removed) { if (!removed) { - scheduleSync(null, type.authority, null, 0 /* no delay */, + scheduleSync(null, UserId.USER_ALL, type.authority, null, 0 /* no delay */, false /* onlyThoseWithUnkownSyncableState */); } } @@ -376,7 +459,7 @@ public class SyncManager implements OnAccountsUpdateListener { AccountManager.get(mContext).addOnAccountsUpdatedListener(SyncManager.this, mSyncHandler, false /* updateImmediately */); // do this synchronously to ensure we have the accounts before this call returns - onAccountsUpdated(AccountManager.get(mContext).getAccounts()); + onAccountsUpdated(null); } } @@ -404,82 +487,6 @@ public class SyncManager implements OnAccountsUpdateListener { } } - private void initializeSyncAdapter(Account account, String authority) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "initializeSyncAdapter: " + account + ", authority " + authority); - } - SyncAdapterType syncAdapterType = SyncAdapterType.newKey(authority, account.type); - RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo = - mSyncAdapters.getServiceInfo(syncAdapterType); - if (syncAdapterInfo == null) { - Log.w(TAG, "can't find a sync adapter for " + syncAdapterType + ", removing"); - mSyncStorageEngine.removeAuthority(account, authority); - return; - } - - Intent intent = new Intent(); - intent.setAction("android.content.SyncAdapter"); - intent.setComponent(syncAdapterInfo.componentName); - if (!mContext.bindService(intent, - new InitializerServiceConnection(account, authority, mContext, mMainHandler), - Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND - | Context.BIND_ALLOW_OOM_MANAGEMENT)) { - Log.w(TAG, "initializeSyncAdapter: failed to bind to " + intent); - } - } - - private static class InitializerServiceConnection implements ServiceConnection { - private final Account mAccount; - private final String mAuthority; - private final Handler mHandler; - private volatile Context mContext; - private volatile boolean mInitialized; - - public InitializerServiceConnection(Account account, String authority, Context context, - Handler handler) { - mAccount = account; - mAuthority = authority; - mContext = context; - mHandler = handler; - mInitialized = false; - } - - public void onServiceConnected(ComponentName name, IBinder service) { - try { - if (!mInitialized) { - mInitialized = true; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "calling initialize: " + mAccount + ", authority " + mAuthority); - } - ISyncAdapter.Stub.asInterface(service).initialize(mAccount, mAuthority); - } - } catch (RemoteException e) { - // doesn't matter, we will retry again later - Log.d(TAG, "error while initializing: " + mAccount + ", authority " + mAuthority, - e); - } finally { - // give the sync adapter time to initialize before unbinding from it - // TODO: change this API to not rely on this timing, http://b/2500805 - mHandler.postDelayed(new Runnable() { - public void run() { - if (mContext != null) { - mContext.unbindService(InitializerServiceConnection.this); - mContext = null; - } - } - }, INITIALIZATION_UNBIND_DELAY_MS); - } - } - - public void onServiceDisconnected(ComponentName name) { - if (mContext != null) { - mContext.unbindService(InitializerServiceConnection.this); - mContext = null; - } - } - - } - /** * Initiate a sync. This can start a sync for all providers * (pass null to url, set onlyTicklable to false), only those @@ -500,6 +507,8 @@ public class SyncManager implements OnAccountsUpdateListener { * <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 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 @@ -507,7 +516,7 @@ public class SyncManager implements OnAccountsUpdateListener { * @param delay how many milliseconds in the future to wait before performing this * @param onlyThoseWithUnkownSyncableState */ - public void scheduleSync(Account requestedAccount, String requestedAuthority, + public void scheduleSync(Account requestedAccount, int userId, String requestedAuthority, Bundle extras, long delay, boolean onlyThoseWithUnkownSyncableState) { boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); @@ -521,9 +530,9 @@ public class SyncManager implements OnAccountsUpdateListener { delay = -1; // this means schedule at the front of the queue } - Account[] accounts; - if (requestedAccount != null) { - accounts = new Account[]{requestedAccount}; + AccountAndUser[] accounts; + if (requestedAccount != null && userId != UserId.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 @@ -574,24 +583,23 @@ public class SyncManager implements OnAccountsUpdateListener { if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority); } - final boolean masterSyncAutomatically = mSyncStorageEngine.getMasterSyncAutomatically(); - for (String authority : syncableAuthorities) { - for (Account account : accounts) { - int isSyncable = mSyncStorageEngine.getIsSyncable(account, authority); + for (AccountAndUser account : accounts) { + int isSyncable = mSyncStorageEngine.getIsSyncable(account.account, account.userId, + authority); if (isSyncable == 0) { continue; } final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo = mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey(authority, account.type)); + SyncAdapterType.newKey(authority, account.account.type)); if (syncAdapterInfo == null) { continue; } final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs(); final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable(); if (isSyncable < 0 && isAlwaysSyncable) { - mSyncStorageEngine.setIsSyncable(account, authority, 1); + mSyncStorageEngine.setIsSyncable(account.account, account.userId, authority, 1); isSyncable = 1; } if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) { @@ -605,8 +613,10 @@ public class SyncManager implements OnAccountsUpdateListener { boolean syncAllowed = (isSyncable < 0) || ignoreSettings - || (backgroundDataUsageAllowed && masterSyncAutomatically - && mSyncStorageEngine.getSyncAutomatically(account, authority)); + || (backgroundDataUsageAllowed + && mSyncStorageEngine.getMasterSyncAutomatically(account.userId) + && mSyncStorageEngine.getSyncAutomatically(account.account, + account.userId, authority)); if (!syncAllowed) { if (isLoggable) { Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority @@ -615,8 +625,10 @@ public class SyncManager implements OnAccountsUpdateListener { continue; } - Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(account, authority); - long delayUntil = mSyncStorageEngine.getDelayUntilTime(account, authority); + 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(); @@ -630,9 +642,8 @@ public class SyncManager implements OnAccountsUpdateListener { + ", extras " + newExtras); } scheduleSyncOperation( - new SyncOperation(account, source, authority, newExtras, 0, - backoffTime, delayUntil, - allowParallelSyncs)); + new SyncOperation(account.account, account.userId, source, authority, + newExtras, 0, backoffTime, delayUntil, allowParallelSyncs)); } if (!onlyThoseWithUnkownSyncableState) { if (isLoggable) { @@ -644,18 +655,17 @@ public class SyncManager implements OnAccountsUpdateListener { + ", extras " + extras); } scheduleSyncOperation( - new SyncOperation(account, source, authority, extras, delay, - backoffTime, delayUntil, - allowParallelSyncs)); + new SyncOperation(account.account, account.userId, source, authority, + extras, delay, backoffTime, delayUntil, allowParallelSyncs)); } } } } - public void scheduleLocalSync(Account account, String authority) { + public void scheduleLocalSync(Account account, int userId, String authority) { final Bundle extras = new Bundle(); extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true); - scheduleSync(account, authority, extras, LOCAL_SYNC_DELAY, + scheduleSync(account, userId, authority, extras, LOCAL_SYNC_DELAY, false /* onlyThoseWithUnkownSyncableState */); } @@ -691,11 +701,13 @@ public class SyncManager implements OnAccountsUpdateListener { mSyncHandler.sendMessage(msg); } - private void sendCancelSyncsMessage(final Account account, final String authority) { + 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); } @@ -717,10 +729,10 @@ public class SyncManager implements OnAccountsUpdateListener { } private void clearBackoffSetting(SyncOperation op) { - mSyncStorageEngine.setBackoff(op.account, op.authority, + 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.authority, 0); + mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, 0); } } @@ -728,7 +740,7 @@ public class SyncManager implements OnAccountsUpdateListener { final long now = SystemClock.elapsedRealtime(); final Pair<Long, Long> previousSettings = - mSyncStorageEngine.getBackoff(op.account, op.authority); + 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 @@ -759,14 +771,14 @@ public class SyncManager implements OnAccountsUpdateListener { final long backoff = now + newDelayInMs; - mSyncStorageEngine.setBackoff(op.account, op.authority, + mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority, backoff, newDelayInMs); op.backoff = backoff; op.updateEffectiveRunTime(); synchronized (mSyncQueue) { - mSyncQueue.onBackoffChanged(op.account, op.authority, backoff); + mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, backoff); } } @@ -779,7 +791,8 @@ public class SyncManager implements OnAccountsUpdateListener { } else { newDelayUntilTime = 0; } - mSyncStorageEngine.setDelayUntilTime(op.account, op.authority, newDelayUntilTime); + mSyncStorageEngine + .setDelayUntilTime(op.account, op.userId, op.authority, newDelayUntilTime); synchronized (mSyncQueue) { mSyncQueue.onDelayUntilTimeChanged(op.account, op.authority, newDelayUntilTime); } @@ -790,8 +803,8 @@ public class SyncManager implements OnAccountsUpdateListener { * @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, String authority) { - sendCancelSyncsMessage(account, authority); + public void cancelActiveSync(Account account, int userId, String authority) { + sendCancelSyncsMessage(account, userId, authority); } /** @@ -823,11 +836,11 @@ public class SyncManager implements OnAccountsUpdateListener { * @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, String authority) { + public void clearScheduledSyncOperations(Account account, int userId, String authority) { synchronized (mSyncQueue) { - mSyncQueue.remove(account, authority); + mSyncQueue.remove(account, userId, authority); } - mSyncStorageEngine.setBackoff(account, authority, + mSyncStorageEngine.setBackoff(account, userId, authority, SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE); } @@ -875,7 +888,8 @@ public class SyncManager implements OnAccountsUpdateListener { Log.d(TAG, "retrying sync operation that failed because there was already a " + "sync in progress: " + operation); } - scheduleSyncOperation(new SyncOperation(operation.account, operation.syncSource, + scheduleSyncOperation(new SyncOperation(operation.account, operation.userId, + operation.syncSource, operation.authority, operation.extras, DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000, operation.backoff, operation.delayUntil, operation.allowParallelSyncs)); @@ -979,7 +993,8 @@ public class SyncManager implements OnAccountsUpdateListener { mBound = true; final boolean bindResult = mContext.bindService(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND - | Context.BIND_ALLOW_OOM_MANAGEMENT); + | Context.BIND_ALLOW_OOM_MANAGEMENT, + mSyncOperation.userId); if (!bindResult) { mBound = false; } @@ -1034,10 +1049,19 @@ public class SyncManager implements OnAccountsUpdateListener { protected void dumpSyncState(PrintWriter pw) { pw.print("data connected: "); pw.println(mDataConnectionIsConnected); - pw.print("auto sync: "); pw.println(mSyncStorageEngine.getMasterSyncAutomatically()); + 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 Account[] accounts = mAccounts; + final AccountAndUser[] accounts = mAccounts; + pw.print("accounts: "); if (accounts != INITIAL_ACCOUNTS_ARRAY) { pw.println(accounts.length); @@ -1090,18 +1114,20 @@ public class SyncManager implements OnAccountsUpdateListener { // join the installed sync adapter with the accounts list and emit for everything pw.println(); pw.println("Sync Status"); - for (Account account : accounts) { - pw.print(" Account "); pw.print(account.name); - pw.print(" "); pw.print(account.type); + for (AccountAndUser account : accounts) { + pw.print(" Account "); pw.print(account.account.name); + pw.print(" u"); pw.print(account.userId); + pw.print(" "); pw.print(account.account.type); pw.println(":"); for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType : mSyncAdapters.getAllServices()) { - if (!syncAdapterType.type.accountType.equals(account.type)) { + if (!syncAdapterType.type.accountType.equals(account.account.type)) { continue; } - SyncStorageEngine.AuthorityInfo settings = mSyncStorageEngine.getOrCreateAuthority( - account, syncAdapterType.type.authority); + SyncStorageEngine.AuthorityInfo settings = + mSyncStorageEngine.getOrCreateAuthority( + account.account, account.userId, syncAdapterType.type.authority); SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(settings); pw.print(" "); pw.print(settings.authority); pw.println(":"); @@ -1554,7 +1580,16 @@ public class SyncManager implements OnAccountsUpdateListener { private volatile CountDownLatch mReadyToRunLatch = new CountDownLatch(1); public void onBootCompleted() { mBootCompleted = true; - mSyncStorageEngine.doDatabaseCleanup(AccountManager.get(mContext).getAccounts()); + // TODO: Handle bootcompleted event for specific user. Now let's just iterate through + // all the users. + List<UserInfo> users = getAllUsers(); + if (users != null) { + for (UserInfo user : users) { + mSyncStorageEngine.doDatabaseCleanup( + AccountManagerService.getSingleton().getAccounts(user.id), + user.id); + } + } if (mReadyToRunLatch != null) { mReadyToRunLatch.countDown(); } @@ -1635,7 +1670,7 @@ public class SyncManager implements OnAccountsUpdateListener { Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CANCEL: " + payload.first + ", " + payload.second); } - cancelActiveSyncLocked(payload.first, payload.second); + cancelActiveSyncLocked(payload.first, msg.arg1, payload.second); nextPendingSyncTime = maybeStartNextSyncLocked(); break; } @@ -1740,22 +1775,28 @@ public class SyncManager implements OnAccountsUpdateListener { final boolean backgroundDataUsageAllowed = getConnectivityManager().getBackgroundDataSetting(); long earliestFuturePollTime = Long.MAX_VALUE; - if (!backgroundDataUsageAllowed || !mSyncStorageEngine.getMasterSyncAutomatically()) { + if (!backgroundDataUsageAllowed) { return earliestFuturePollTime; } + + AccountAndUser[] accounts = mAccounts; + final long nowAbsolute = System.currentTimeMillis(); ArrayList<SyncStorageEngine.AuthorityInfo> infos = mSyncStorageEngine.getAuthorities(); for (SyncStorageEngine.AuthorityInfo info : infos) { // skip the sync if the account of this operation no longer exists - if (!ArrayUtils.contains(mAccounts, info.account)) { + if (!containsAccountAndUser(accounts, info.account, info.userId)) { continue; } - if (!mSyncStorageEngine.getSyncAutomatically(info.account, info.authority)) { + if (!mSyncStorageEngine.getMasterSyncAutomatically(info.userId) + || !mSyncStorageEngine.getSyncAutomatically(info.account, info.userId, + info.authority)) { continue; } - if (mSyncStorageEngine.getIsSyncable(info.account, info.authority) == 0) { + if (mSyncStorageEngine.getIsSyncable(info.account, info.userId, info.authority) + == 0) { continue; } @@ -1772,8 +1813,8 @@ public class SyncManager implements OnAccountsUpdateListener { : lastPollTimeAbsolute + periodInSeconds * 1000; // if it is ready to run then schedule it and mark it as having been scheduled if (nextPollTimeAbsolute <= nowAbsolute) { - final Pair<Long, Long> backoff = - mSyncStorageEngine.getBackoff(info.account, info.authority); + final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff( + info.account, info.userId, info.authority); final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo = mSyncAdapters.getServiceInfo( SyncAdapterType.newKey(info.authority, info.account.type)); @@ -1781,11 +1822,12 @@ public class SyncManager implements OnAccountsUpdateListener { continue; } scheduleSyncOperation( - new SyncOperation(info.account, SyncStorageEngine.SOURCE_PERIODIC, + new SyncOperation(info.account, info.userId, + SyncStorageEngine.SOURCE_PERIODIC, info.authority, extras, 0 /* delay */, backoff != null ? backoff.first : 0, mSyncStorageEngine.getDelayUntilTime( - info.account, info.authority), + info.account, info.userId, info.authority), syncAdapterInfo.type.allowParallelSyncs())); status.setPeriodicSyncTime(i, nowAbsolute); } else { @@ -1830,7 +1872,7 @@ public class SyncManager implements OnAccountsUpdateListener { // 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. - Account[] accounts = mAccounts; + AccountAndUser[] accounts = mAccounts; if (accounts == INITIAL_ACCOUNTS_ARRAY) { if (isLoggable) { Log.v(TAG, "maybeStartNextSync: accounts not known, skipping"); @@ -1843,7 +1885,6 @@ public class SyncManager implements OnAccountsUpdateListener { // start it, otherwise just get out. final boolean backgroundDataUsageAllowed = getConnectivityManager().getBackgroundDataSetting(); - final boolean masterSyncAutomatically = mSyncStorageEngine.getMasterSyncAutomatically(); final long now = SystemClock.elapsedRealtime(); @@ -1863,14 +1904,15 @@ public class SyncManager implements OnAccountsUpdateListener { final SyncOperation op = operationIterator.next(); // drop the sync if the account of this operation no longer exists - if (!ArrayUtils.contains(mAccounts, op.account)) { + 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.authority); + int syncableState = mSyncStorageEngine.getIsSyncable( + op.account, op.userId, op.authority); if (syncableState == 0) { operationIterator.remove(); mSyncStorageEngine.deleteFromPending(op.pendingOperation); @@ -1905,11 +1947,11 @@ public class SyncManager implements OnAccountsUpdateListener { // disconnected for the target UID. if (!op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false) && (syncableState > 0) - && (!masterSyncAutomatically + && (!mSyncStorageEngine.getMasterSyncAutomatically(op.userId) || !backgroundDataUsageAllowed || !uidNetworkConnected || !mSyncStorageEngine.getSyncAutomatically( - op.account, op.authority))) { + op.account, op.userId, op.authority))) { operationIterator.remove(); mSyncStorageEngine.deleteFromPending(op.pendingOperation); continue; @@ -1946,6 +1988,7 @@ public class SyncManager implements OnAccountsUpdateListener { } 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; @@ -2033,7 +2076,7 @@ public class SyncManager implements OnAccountsUpdateListener { if (syncAdapterInfo == null) { Log.d(TAG, "can't find a sync adapter for " + syncAdapterType + ", removing settings for it"); - mSyncStorageEngine.removeAuthority(op.account, op.authority); + mSyncStorageEngine.removeAuthority(op.account, op.userId, op.authority); return false; } @@ -2074,7 +2117,7 @@ public class SyncManager implements OnAccountsUpdateListener { } } - private void cancelActiveSyncLocked(Account account, String authority) { + private void cancelActiveSyncLocked(Account account, int userId, String authority) { ArrayList<ActiveSyncContext> activeSyncs = new ArrayList<ActiveSyncContext>(mActiveSyncContexts); for (ActiveSyncContext activeSyncContext : activeSyncs) { @@ -2082,15 +2125,20 @@ public class SyncManager implements OnAccountsUpdateListener { // if an authority was specified then only cancel the sync if it matches if (account != null) { if (!account.equals(activeSyncContext.mSyncOperation.account)) { - return; + continue; } } // if an account was specified then only cancel the sync if it matches if (authority != null) { if (!authority.equals(activeSyncContext.mSyncOperation.authority)) { - return; + continue; } } + // check if the userid matches + if (userId != UserId.USER_ALL + && userId != activeSyncContext.mSyncOperation.userId) { + continue; + } runSyncFinishedOrCanceledLocked(null /* no result since this is a cancel */, activeSyncContext); } @@ -2169,7 +2217,7 @@ public class SyncManager implements OnAccountsUpdateListener { } if (syncResult != null && syncResult.fullSyncRequested) { - scheduleSyncOperation(new SyncOperation(syncOperation.account, + scheduleSyncOperation(new SyncOperation(syncOperation.account, syncOperation.userId, syncOperation.syncSource, syncOperation.authority, new Bundle(), 0, syncOperation.backoff, syncOperation.delayUntil, syncOperation.allowParallelSyncs)); @@ -2180,7 +2228,8 @@ public class SyncManager implements OnAccountsUpdateListener { private void closeActiveSyncContext(ActiveSyncContext activeSyncContext) { activeSyncContext.close(); mActiveSyncContexts.remove(activeSyncContext); - mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo); + mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo, + activeSyncContext.mSyncOperation.userId); } /** @@ -2446,7 +2495,8 @@ public class SyncManager implements OnAccountsUpdateListener { syncOperation.account.name.hashCode()); return mSyncStorageEngine.insertStartSyncEvent( - syncOperation.account, syncOperation.authority, now, source); + syncOperation.account, syncOperation.userId, syncOperation.authority, + now, source); } public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage, diff --git a/core/java/android/content/SyncOperation.java b/core/java/android/content/SyncOperation.java index 94c224706c97..4e86ef8a2eb2 100644 --- a/core/java/android/content/SyncOperation.java +++ b/core/java/android/content/SyncOperation.java @@ -26,6 +26,7 @@ import android.os.SystemClock; */ public class SyncOperation implements Comparable { public final Account account; + public final int userId; public int syncSource; public String authority; public final boolean allowParallelSyncs; @@ -38,9 +39,10 @@ public class SyncOperation implements Comparable { public long delayUntil; public long effectiveRunTime; - public SyncOperation(Account account, int source, String authority, Bundle extras, + public SyncOperation(Account account, int userId, int source, String authority, Bundle extras, long delayInMs, long backoff, long delayUntil, boolean allowParallelSyncs) { this.account = account; + this.userId = userId; this.syncSource = source; this.authority = authority; this.allowParallelSyncs = allowParallelSyncs; @@ -75,6 +77,7 @@ public class SyncOperation implements Comparable { SyncOperation(SyncOperation other) { this.account = other.account; + this.userId = other.userId; this.syncSource = other.syncSource; this.authority = other.authority; this.extras = new Bundle(other.extras); @@ -120,7 +123,8 @@ public class SyncOperation implements Comparable { private String toKey() { StringBuilder sb = new StringBuilder(); sb.append("authority: ").append(authority); - sb.append(" account {name=" + account.name + ", type=" + account.type + "}"); + sb.append(" account {name=" + account.name + ", user=" + userId + ", type=" + account.type + + "}"); sb.append(" extras: "); extrasToStringBuilder(extras, sb); return sb.toString(); diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java index bfdf4a1da3c7..06da6fadd82f 100644 --- a/core/java/android/content/SyncQueue.java +++ b/core/java/android/content/SyncQueue.java @@ -49,7 +49,8 @@ public class SyncQueue { final int N = ops.size(); for (int i=0; i<N; i++) { SyncStorageEngine.PendingOperation op = ops.get(i); - final Pair<Long, Long> backoff = syncStorageEngine.getBackoff(op.account, op.authority); + final Pair<Long, Long> backoff = + syncStorageEngine.getBackoff(op.account, op.userId, op.authority); final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo = syncAdapters.getServiceInfo( SyncAdapterType.newKey(op.authority, op.account.type)); @@ -57,9 +58,9 @@ public class SyncQueue { continue; } SyncOperation syncOperation = new SyncOperation( - op.account, op.syncSource, op.authority, op.extras, 0 /* delay */, + op.account, op.userId, op.syncSource, op.authority, op.extras, 0 /* delay */, backoff != null ? backoff.first : 0, - syncStorageEngine.getDelayUntilTime(op.account, op.authority), + syncStorageEngine.getDelayUntilTime(op.account, op.userId, op.authority), syncAdapterInfo.type.allowParallelSyncs()); syncOperation.expedited = op.expedited; syncOperation.pendingOperation = op; @@ -102,8 +103,8 @@ public class SyncQueue { operation.pendingOperation = pop; if (operation.pendingOperation == null) { pop = new SyncStorageEngine.PendingOperation( - operation.account, operation.syncSource, - operation.authority, operation.extras, operation.expedited); + operation.account, operation.userId, operation.syncSource, + operation.authority, operation.extras, operation.expedited); pop = mSyncStorageEngine.insertIntoPending(pop); if (pop == null) { throw new IllegalStateException("error adding pending sync operation " @@ -131,11 +132,12 @@ public class SyncQueue { } } - public void onBackoffChanged(Account account, String providerName, long backoff) { + 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)) { + if (op.account.equals(account) && op.authority.equals(providerName) + && op.userId == userId) { op.backoff = backoff; op.updateEffectiveRunTime(); } @@ -153,7 +155,7 @@ public class SyncQueue { } } - public void remove(Account account, String authority) { + 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(); @@ -164,6 +166,9 @@ public class SyncQueue { 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; diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java index a1e174b119b2..7bb986610b47 100644 --- a/core/java/android/content/SyncStorageEngine.java +++ b/core/java/android/content/SyncStorageEngine.java @@ -25,6 +25,7 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import android.accounts.Account; +import android.content.SyncManager.AccountAndUser; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; @@ -58,9 +59,16 @@ import java.util.List; * @hide */ public class SyncStorageEngine extends Handler { + private static final String TAG = "SyncManager"; 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_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 @@ -133,6 +141,7 @@ public class SyncStorageEngine extends Handler { public static class PendingOperation { final Account account; + final int userId; final int syncSource; final String authority; final Bundle extras; // note: read-only. @@ -141,9 +150,10 @@ public class SyncStorageEngine extends Handler { int authorityId; byte[] flatExtras; - PendingOperation(Account account, int source, + PendingOperation(Account account, int userId, int source, String authority, Bundle extras, boolean expedited) { this.account = account; + this.userId = userId; this.syncSource = source; this.authority = authority; this.extras = extras != null ? new Bundle(extras) : extras; @@ -153,6 +163,7 @@ public class SyncStorageEngine extends Handler { PendingOperation(PendingOperation other) { this.account = other.account; + this.userId = other.userId; this.syncSource = other.syncSource; this.authority = other.authority; this.extras = other.extras; @@ -162,17 +173,18 @@ public class SyncStorageEngine extends Handler { } static class AccountInfo { - final Account account; + final AccountAndUser accountAndUser; final HashMap<String, AuthorityInfo> authorities = new HashMap<String, AuthorityInfo>(); - AccountInfo(Account account) { - this.account = account; + AccountInfo(AccountAndUser accountAndUser) { + this.accountAndUser = accountAndUser; } } public static class AuthorityInfo { final Account account; + final int userId; final String authority; final int ident; boolean enabled; @@ -182,8 +194,9 @@ public class SyncStorageEngine extends Handler { long delayUntil; final ArrayList<Pair<Bundle, Long>> periodicSyncs; - AuthorityInfo(Account account, String authority, int ident) { + 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; @@ -219,17 +232,29 @@ public class SyncStorageEngine extends Handler { } } + interface OnSyncRequestListener { + /** + * Called when a sync is needed on an account(s) due to some change in state. + * @param account + * @param userId + * @param authority + * @param extras + */ + public void onSyncRequest(Account account, int userId, 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<Account, AccountInfo> mAccounts = - new HashMap<Account, AccountInfo>(); + private final HashMap<AccountAndUser, AccountInfo> mAccounts + = new HashMap<AccountAndUser, AccountInfo>(); private final ArrayList<PendingOperation> mPendingOperations = new ArrayList<PendingOperation>(); - private final ArrayList<SyncInfo> mCurrentSyncs = new ArrayList<SyncInfo>(); + private final SparseArray<ArrayList<SyncInfo>> mCurrentSyncs + = new SparseArray<ArrayList<SyncInfo>>(); private final SparseArray<SyncStatusInfo> mSyncStatus = new SparseArray<SyncStatusInfo>(); @@ -282,7 +307,9 @@ public class SyncStorageEngine extends Handler { private int mNumPendingFinished = 0; private int mNextHistoryId = 0; - private boolean mMasterSyncAutomatically = true; + private SparseArray<Boolean> mMasterSyncAutomatically = new SparseArray<Boolean>(); + + private OnSyncRequestListener mSyncRequestListener; private SyncStorageEngine(Context context, File dataDir) { mContext = context; @@ -330,6 +357,12 @@ public class SyncStorageEngine extends Handler { 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) { @@ -389,10 +422,10 @@ public class SyncStorageEngine extends Handler { } } - public boolean getSyncAutomatically(Account account, String providerName) { + public boolean getSyncAutomatically(Account account, int userId, String providerName) { synchronized (mAuthorities) { if (account != null) { - AuthorityInfo authority = getAuthorityLocked(account, providerName, + AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, "getSyncAutomatically"); return authority != null && authority.enabled; } @@ -402,6 +435,7 @@ public class SyncStorageEngine extends Handler { i--; AuthorityInfo authority = mAuthorities.valueAt(i); if (authority.authority.equals(providerName) + && authority.userId == userId && authority.enabled) { return true; } @@ -410,11 +444,13 @@ public class SyncStorageEngine extends Handler { } } - public void setSyncAutomatically(Account account, String providerName, boolean sync) { - Log.d(TAG, "setSyncAutomatically: " + /*account +*/ ", provider " + providerName - + " -> " + sync); + public void setSyncAutomatically(Account account, int userId, String providerName, + boolean sync) { + Log.d(TAG, "setSyncAutomatically: " + /* account + */" provider " + providerName + + ", user " + userId + " -> " + sync); synchronized (mAuthorities) { - AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false); + AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1, + false); if (authority.enabled == sync) { Log.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing"); return; @@ -424,15 +460,15 @@ public class SyncStorageEngine extends Handler { } if (sync) { - ContentResolver.requestSync(account, providerName, new Bundle()); + requestSync(account, userId, providerName, new Bundle()); } reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); } - public int getIsSyncable(Account account, String providerName) { + public int getIsSyncable(Account account, int userId, String providerName) { synchronized (mAuthorities) { if (account != null) { - AuthorityInfo authority = getAuthorityLocked(account, providerName, + AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, "getIsSyncable"); if (authority == null) { return -1; @@ -452,15 +488,17 @@ public class SyncStorageEngine extends Handler { } } - public void setIsSyncable(Account account, String providerName, int syncable) { + public void setIsSyncable(Account account, int userId, String providerName, int syncable) { if (syncable > 1) { syncable = 1; } else if (syncable < -1) { syncable = -1; } - Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName + " -> " + syncable); + Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName + + ", user " + userId + " -> " + syncable); synchronized (mAuthorities) { - AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false); + AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1, + false); if (authority.syncable == syncable) { Log.d(TAG, "setIsSyncable: already set to " + syncable + ", doing nothing"); return; @@ -470,14 +508,15 @@ public class SyncStorageEngine extends Handler { } if (syncable > 0) { - ContentResolver.requestSync(account, providerName, new Bundle()); + requestSync(account, userId, providerName, new Bundle()); } reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); } - public Pair<Long, Long> getBackoff(Account account, String providerName) { + public Pair<Long, Long> getBackoff(Account account, int userId, String providerName) { synchronized (mAuthorities) { - AuthorityInfo authority = getAuthorityLocked(account, providerName, "getBackoff"); + AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, + "getBackoff"); if (authority == null || authority.backoffTime < 0) { return null; } @@ -485,17 +524,21 @@ public class SyncStorageEngine extends Handler { } } - public void setBackoff(Account account, String providerName, + public void setBackoff(Account account, int userId, String providerName, long nextSyncTime, long nextDelay) { if (Log.isLoggable(TAG, Log.VERBOSE)) { 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.account)) continue; + 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; @@ -510,7 +553,8 @@ public class SyncStorageEngine extends Handler { } } else { AuthorityInfo authority = - getOrCreateAuthorityLocked(account, providerName, -1 /* ident */, true); + getOrCreateAuthorityLocked(account, userId, providerName, -1 /* ident */, + true); if (authority.backoffTime == nextSyncTime && authority.backoffDelay == nextDelay) { return; } @@ -535,13 +579,15 @@ public class SyncStorageEngine extends Handler { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "clearAllBackoffs:" + " authority:" + authorityInfo.authority - + " account:" + accountInfo.account.name + + " 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.account, authorityInfo.authority, 0); + syncQueue.onBackoffChanged(accountInfo.accountAndUser.account, + accountInfo.accountAndUser.userId, authorityInfo.authority, 0); changed = true; } } @@ -553,14 +599,15 @@ public class SyncStorageEngine extends Handler { } } - public void setDelayUntilTime(Account account, String providerName, long delayUntil) { + public void setDelayUntilTime(Account account, int userId, String providerName, + long delayUntil) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "setDelayUntil: " + account + ", provider " + providerName - + " -> delayUntil " + delayUntil); + + ", user " + userId + " -> delayUntil " + delayUntil); } synchronized (mAuthorities) { AuthorityInfo authority = getOrCreateAuthorityLocked( - account, providerName, -1 /* ident */, true); + account, userId, providerName, -1 /* ident */, true); if (authority.delayUntil == delayUntil) { return; } @@ -570,9 +617,10 @@ public class SyncStorageEngine extends Handler { reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); } - public long getDelayUntilTime(Account account, String providerName) { + public long getDelayUntilTime(Account account, int userId, String providerName) { synchronized (mAuthorities) { - AuthorityInfo authority = getAuthorityLocked(account, providerName, "getDelayUntil"); + AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, + "getDelayUntil"); if (authority == null) { return 0; } @@ -580,7 +628,8 @@ public class SyncStorageEngine extends Handler { } } - private void updateOrRemovePeriodicSync(Account account, String providerName, Bundle extras, + private void updateOrRemovePeriodicSync(Account account, int userId, String providerName, + Bundle extras, long period, boolean add) { if (period <= 0) { period = 0; @@ -589,13 +638,14 @@ public class SyncStorageEngine extends Handler { extras = new Bundle(); } if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "addOrRemovePeriodicSync: " + account + ", provider " + providerName + Log.v(TAG, "addOrRemovePeriodicSync: " + account + ", user " + userId + + ", provider " + providerName + " -> period " + period + ", extras " + extras); } synchronized (mAuthorities) { try { AuthorityInfo authority = - getOrCreateAuthorityLocked(account, providerName, -1, false); + 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 @@ -652,61 +702,67 @@ public class SyncStorageEngine extends Handler { reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); } - public void addPeriodicSync(Account account, String providerName, Bundle extras, + public void addPeriodicSync(Account account, int userId, String providerName, Bundle extras, long pollFrequency) { - updateOrRemovePeriodicSync(account, providerName, extras, pollFrequency, true /* add */); + updateOrRemovePeriodicSync(account, userId, providerName, extras, pollFrequency, + true /* add */); } - public void removePeriodicSync(Account account, String providerName, Bundle extras) { - updateOrRemovePeriodicSync(account, providerName, extras, 0 /* period, ignored */, + 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, String providerName) { + public List<PeriodicSync> getPeriodicSyncs(Account account, int userId, String providerName) { ArrayList<PeriodicSync> syncs = new ArrayList<PeriodicSync>(); synchronized (mAuthorities) { - AuthorityInfo authority = getAuthorityLocked(account, providerName, "getPeriodicSyncs"); + 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)); + syncs.add(new PeriodicSync(account, providerName, item.first, + item.second)); } } } return syncs; } - public void setMasterSyncAutomatically(boolean flag) { + public void setMasterSyncAutomatically(boolean flag, int userId) { synchronized (mAuthorities) { - if (mMasterSyncAutomatically == flag) { + Boolean auto = mMasterSyncAutomatically.get(userId); + if (auto != null && (boolean) auto == flag) { return; } - mMasterSyncAutomatically = flag; + mMasterSyncAutomatically.put(userId, flag); writeAccountInfoLocked(); } if (flag) { - ContentResolver.requestSync(null, null, new Bundle()); + requestSync(null, userId, null, new Bundle()); } reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); mContext.sendBroadcast(SYNC_CONNECTION_SETTING_CHANGED_INTENT); } - public boolean getMasterSyncAutomatically() { + public boolean getMasterSyncAutomatically(int userId) { synchronized (mAuthorities) { - return mMasterSyncAutomatically; + Boolean auto = mMasterSyncAutomatically.get(userId); + return auto == null ? true : auto; } } - public AuthorityInfo getOrCreateAuthority(Account account, String authority) { + public AuthorityInfo getOrCreateAuthority(Account account, int userId, String authority) { synchronized (mAuthorities) { - return getOrCreateAuthorityLocked(account, authority, + 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, String authority) { + public void removeAuthority(Account account, int userId, String authority) { synchronized (mAuthorities) { - removeAuthorityLocked(account, authority, true /* doWrite */); + removeAuthorityLocked(account, userId, authority, true /* doWrite */); } } @@ -720,12 +776,13 @@ public class SyncStorageEngine extends Handler { * Returns true if there is currently a sync operation for the given * account or authority actively being processed. */ - public boolean isSyncActive(Account account, String authority) { + public boolean isSyncActive(Account account, int userId, String authority) { synchronized (mAuthorities) { - for (SyncInfo syncInfo : mCurrentSyncs) { + for (SyncInfo syncInfo : getCurrentSyncs(userId)) { AuthorityInfo ainfo = getAuthority(syncInfo.authorityId); if (ainfo != null && ainfo.account.equals(account) - && ainfo.authority.equals(authority)) { + && ainfo.authority.equals(authority) + && ainfo.userId == userId) { return true; } } @@ -738,12 +795,13 @@ public class SyncStorageEngine extends Handler { synchronized (mAuthorities) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "insertIntoPending: account=" + op.account - + " auth=" + op.authority - + " src=" + op.syncSource - + " extras=" + op.extras); + + " user=" + op.userId + + " auth=" + op.authority + + " src=" + op.syncSource + + " extras=" + op.extras); } - AuthorityInfo authority = getOrCreateAuthorityLocked(op.account, + AuthorityInfo authority = getOrCreateAuthorityLocked(op.account, op.userId, op.authority, -1 /* desired identifier */, true /* write accounts to storage */); @@ -769,6 +827,7 @@ public class SyncStorageEngine extends Handler { synchronized (mAuthorities) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "deleteFromPending: account=" + op.account + + " user=" + op.userId + " auth=" + op.authority + " src=" + op.syncSource + " extras=" + op.extras); @@ -782,7 +841,7 @@ public class SyncStorageEngine extends Handler { mNumPendingFinished++; } - AuthorityInfo authority = getAuthorityLocked(op.account, op.authority, + AuthorityInfo authority = getAuthorityLocked(op.account, op.userId, op.authority, "deleteFromPending"); if (authority != null) { if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "removing - " + authority); @@ -791,7 +850,8 @@ public class SyncStorageEngine extends Handler { for (int i=0; i<N; i++) { PendingOperation cur = mPendingOperations.get(i); if (cur.account.equals(op.account) - && cur.authority.equals(op.authority)) { + && cur.authority.equals(op.authority) + && cur.userId == op.userId) { morePending = true; break; } @@ -812,24 +872,6 @@ public class SyncStorageEngine extends Handler { return res; } - public int clearPending() { - int num; - synchronized (mAuthorities) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "clearPending"); - } - num = mPendingOperations.size(); - mPendingOperations.clear(); - final int N = mSyncStatus.size(); - for (int i=0; i<N; i++) { - mSyncStatus.valueAt(i).pending = false; - } - writePendingOperationsLocked(); - } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING); - return num; - } - /** * Return a copy of the current array of pending operations. The * PendingOperation objects are the real objects stored inside, so that @@ -854,17 +896,18 @@ public class SyncStorageEngine extends Handler { * Called when the set of account has changed, given the new array of * active accounts. */ - public void doDatabaseCleanup(Account[] accounts) { + public void doDatabaseCleanup(Account[] accounts, int userId) { synchronized (mAuthorities) { if (Log.isLoggable(TAG, Log.VERBOSE)) Log.w(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.account)) { + if (!ArrayUtils.contains(accounts, acc.accountAndUser.account) + && acc.accountAndUser.userId == userId) { // This account no longer exists... if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.w(TAG, "Account removed: " + acc.account); + Log.w(TAG, "Account removed: " + acc.accountAndUser); } for (AuthorityInfo auth : acc.authorities.values()) { removing.put(auth.ident, auth); @@ -919,13 +962,14 @@ public class SyncStorageEngine extends Handler { } 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); - mCurrentSyncs.add(syncInfo); + getCurrentSyncs(authority.userId).add(syncInfo); } reportActiveChange(); @@ -935,13 +979,14 @@ public class SyncStorageEngine extends Handler { /** * Called to indicate that a previously active sync is no longer active. */ - public void removeActiveSync(SyncInfo syncInfo) { + public void removeActiveSync(SyncInfo syncInfo, int userId) { synchronized (mAuthorities) { if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "removeActiveSync: account=" - + syncInfo.account + " auth=" + syncInfo.authority); + Log.v(TAG, "removeActiveSync: account=" + syncInfo.account + + " user=" + userId + + " auth=" + syncInfo.authority); } - mCurrentSyncs.remove(syncInfo); + getCurrentSyncs(userId).remove(syncInfo); } reportActiveChange(); @@ -957,15 +1002,15 @@ public class SyncStorageEngine extends Handler { /** * Note that sync has started for the given account and authority. */ - public long insertStartSyncEvent(Account accountName, String authorityName, + public long insertStartSyncEvent(Account accountName, int userId, String authorityName, long now, int source) { long id; synchronized (mAuthorities) { if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "insertStartSyncEvent: account=" + accountName + Log.v(TAG, "insertStartSyncEvent: account=" + accountName + "user=" + userId + " auth=" + authorityName + " source=" + source); } - AuthorityInfo authority = getAuthorityLocked(accountName, authorityName, + AuthorityInfo authority = getAuthorityLocked(accountName, userId, authorityName, "insertStartSyncEvent"); if (authority == null) { return -1; @@ -1119,9 +1164,14 @@ public class SyncStorageEngine extends Handler { * 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() { + public List<SyncInfo> getCurrentSyncs(int userId) { synchronized (mAuthorities) { - return new ArrayList<SyncInfo>(mCurrentSyncs); + ArrayList<SyncInfo> syncs = mCurrentSyncs.get(userId); + if (syncs == null) { + syncs = new ArrayList<SyncInfo>(); + mCurrentSyncs.put(userId, syncs); + } + return new ArrayList<SyncInfo>(syncs); } } @@ -1164,7 +1214,8 @@ public class SyncStorageEngine extends Handler { * @param authority the authority whose row should be selected * @return the SyncStatusInfo for the authority */ - public SyncStatusInfo getStatusByAccountAndAuthority(Account account, String authority) { + public SyncStatusInfo getStatusByAccountAndAuthority(Account account, int userId, + String authority) { if (account == null || authority == null) { throw new IllegalArgumentException(); } @@ -1174,8 +1225,9 @@ public class SyncStorageEngine extends Handler { SyncStatusInfo cur = mSyncStatus.valueAt(i); AuthorityInfo ainfo = mAuthorities.get(cur.authorityId); - if (ainfo != null && ainfo.authority.equals(authority) && - account.equals(ainfo.account)) { + if (ainfo != null && ainfo.authority.equals(authority) + && ainfo.userId == userId + && account.equals(ainfo.account)) { return cur; } } @@ -1186,7 +1238,7 @@ public class SyncStorageEngine extends Handler { /** * Return true if the pending status is true of any matching authorities. */ - public boolean isSyncPending(Account account, String authority) { + public boolean isSyncPending(Account account, int userId, String authority) { synchronized (mAuthorities) { final int N = mSyncStatus.size(); for (int i=0; i<N; i++) { @@ -1195,6 +1247,9 @@ public class SyncStorageEngine extends Handler { if (ainfo == null) { continue; } + if (userId != ainfo.userId) { + continue; + } if (account != null && !ainfo.account.equals(account)) { continue; } @@ -1235,34 +1290,6 @@ public class SyncStorageEngine extends Handler { } } - /** - * If sync is failing for any of the provider/accounts then determine the time at which it - * started failing and return the earliest time over all the provider/accounts. If none are - * failing then return 0. - */ - public long getInitialSyncFailureTime() { - synchronized (mAuthorities) { - if (!mMasterSyncAutomatically) { - return 0; - } - - long oldest = 0; - int i = mSyncStatus.size(); - while (i > 0) { - i--; - SyncStatusInfo stats = mSyncStatus.valueAt(i); - AuthorityInfo authority = mAuthorities.get(stats.authorityId); - if (authority != null && authority.enabled) { - if (oldest == 0 || stats.initialFailureTime < oldest) { - oldest = stats.initialFailureTime; - } - } - } - - return oldest; - } - } - private int getCurrentDayLocked() { mCal.setTimeInMillis(System.currentTimeMillis()); final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR); @@ -1283,18 +1310,19 @@ public class SyncStorageEngine extends Handler { * @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, String authorityName, + private AuthorityInfo getAuthorityLocked(Account accountName, int userId, String authorityName, String tag) { - AccountInfo account = mAccounts.get(accountName); - if (account == null) { + AccountAndUser au = new AccountAndUser(accountName, userId); + AccountInfo accountInfo = mAccounts.get(au); + if (accountInfo == null) { if (tag != null) { if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, tag + ": unknown account " + accountName); + Log.v(TAG, tag + ": unknown account " + au); } } return null; } - AuthorityInfo authority = account.authorities.get(authorityName); + AuthorityInfo authority = accountInfo.authorities.get(authorityName); if (authority == null) { if (tag != null) { if (Log.isLoggable(TAG, Log.VERBOSE)) { @@ -1307,12 +1335,13 @@ public class SyncStorageEngine extends Handler { return authority; } - private AuthorityInfo getOrCreateAuthorityLocked(Account accountName, + private AuthorityInfo getOrCreateAuthorityLocked(Account accountName, int userId, String authorityName, int ident, boolean doWrite) { - AccountInfo account = mAccounts.get(accountName); + AccountAndUser au = new AccountAndUser(accountName, userId); + AccountInfo account = mAccounts.get(au); if (account == null) { - account = new AccountInfo(accountName); - mAccounts.put(accountName, account); + account = new AccountInfo(au); + mAccounts.put(au, account); } AuthorityInfo authority = account.authorities.get(authorityName); if (authority == null) { @@ -1323,9 +1352,10 @@ public class SyncStorageEngine extends Handler { } if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "created a new AuthorityInfo for " + accountName - + ", provider " + authorityName); + + ", user " + userId + + ", provider " + authorityName); } - authority = new AuthorityInfo(accountName, authorityName, ident); + authority = new AuthorityInfo(accountName, userId, authorityName, ident); account.authorities.put(authorityName, authority); mAuthorities.put(ident, authority); if (doWrite) { @@ -1336,8 +1366,9 @@ public class SyncStorageEngine extends Handler { return authority; } - private void removeAuthorityLocked(Account account, String authorityName, boolean doWrite) { - AccountInfo accountInfo = mAccounts.get(account); + 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) { @@ -1419,8 +1450,7 @@ public class SyncStorageEngine extends Handler { } String tagName = parser.getName(); if ("accounts".equals(tagName)) { - String listen = parser.getAttributeValue( - null, "listen-for-tickles"); + String listen = parser.getAttributeValue(null, XML_ATTR_LISTEN_FOR_TICKLES); String versionString = parser.getAttributeValue(null, "version"); int version; try { @@ -1428,14 +1458,14 @@ public class SyncStorageEngine extends Handler { } catch (NumberFormatException e) { version = 0; } - String nextIdString = parser.getAttributeValue(null, "nextAuthorityId"); + 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 } - mMasterSyncAutomatically = listen == null || Boolean.parseBoolean(listen); + mMasterSyncAutomatically.put(0, listen == null || Boolean.parseBoolean(listen)); eventType = parser.next(); AuthorityInfo authority = null; Pair<Bundle, Long> periodicSync = null; @@ -1449,6 +1479,8 @@ public class SyncStorageEngine extends Handler { 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) { @@ -1511,25 +1543,41 @@ public class SyncStorageEngine extends Handler { } // if we already have a record of this new authority then don't copy over the settings - if (getAuthorityLocked(authority.account, newAuthorityName, "cleanup") != null) { + if (getAuthorityLocked(authority.account, authority.userId, newAuthorityName, "cleanup") + != null) { continue; } AuthorityInfo newAuthority = getOrCreateAuthorityLocked(authority.account, - newAuthorityName, -1 /* ident */, false /* doWrite */); + authority.userId, newAuthorityName, -1 /* ident */, false /* doWrite */); newAuthority.enabled = true; writeNeeded = true; } for (AuthorityInfo authorityInfo : authoritiesToRemove) { - removeAuthorityLocked(authorityInfo.account, authorityInfo.authority, - false /* doWrite */); + 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; @@ -1543,10 +1591,12 @@ public class SyncStorageEngine extends Handler { } if (id >= 0) { String authorityName = parser.getAttributeValue(null, "authority"); - String enabled = parser.getAttributeValue(null, "enabled"); + 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"; @@ -1554,12 +1604,13 @@ public class SyncStorageEngine extends Handler { 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), authorityName, id, false); + 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. @@ -1653,9 +1704,17 @@ public class SyncStorageEngine extends Handler { out.startTag(null, "accounts"); out.attribute(null, "version", Integer.toString(ACCOUNTS_VERSION)); - out.attribute(null, "nextAuthorityId", Integer.toString(mNextAuthorityId)); - if (!mMasterSyncAutomatically) { - out.attribute(null, "listen-for-tickles", "false"); + out.attribute(null, XML_ATTR_NEXT_AUTHORITY_ID, Integer.toString(mNextAuthorityId)); + + // 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(); @@ -1664,9 +1723,10 @@ public class SyncStorageEngine extends Handler { 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, "enabled", Boolean.toString(authority.enabled)); + out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(authority.enabled)); if (authority.syncable < 0) { out.attribute(null, "syncable", "unknown"); } else { @@ -1788,7 +1848,7 @@ public class SyncStorageEngine extends Handler { } String authorityName = c.getString(c.getColumnIndex("authority")); AuthorityInfo authority = this.getOrCreateAuthorityLocked( - new Account(accountName, accountType), + new Account(accountName, accountType), 0 /* legacy is single-user */, authorityName, -1, false); if (authority != null) { int i = mSyncStatus.size(); @@ -1833,7 +1893,7 @@ public class SyncStorageEngine extends Handler { String value = c.getString(c.getColumnIndex("value")); if (name == null) continue; if (name.equals("listen_for_tickles")) { - setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value)); + setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value), 0); } else if (name.startsWith("sync_provider_")) { String provider = name.substring("sync_provider_".length(), name.length()); @@ -1964,7 +2024,7 @@ public class SyncStorageEngine extends Handler { extras = new Bundle(); } PendingOperation op = new PendingOperation( - authority.account, syncSource, + authority.account, authority.userId, syncSource, authority.authority, extras, expedited); op.authorityId = authorityId; op.flatExtras = flatExtras; @@ -2084,6 +2144,19 @@ public class SyncStorageEngine extends Handler { return bundle; } + private void requestSync(Account account, int userId, 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, 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; diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 215e836bf735..6c1445df74a0 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -28,6 +28,8 @@ import java.util.regex.Pattern; import java.util.zip.CRC32; import java.util.zip.CheckedInputStream; +import libcore.io.Os; +import libcore.io.StructStat; /** * Tools for managing files. Not for public consumption. @@ -52,8 +54,10 @@ public class FileUtils { /** * File status information. This class maps directly to the POSIX stat structure. + * @deprecated use {@link StructStat} instead. * @hide */ + @Deprecated public static final class FileStatus { public int dev; public int ino; @@ -77,7 +81,9 @@ public class FileUtils { * exists. * @return true if the file exists and false if it does not exist. If you do not have * permission to stat the file, then this method will return false. + * @deprecated use {@link Os#stat(String)} instead. */ + @Deprecated public static boolean getFileStatus(String path, FileStatus status) { StrictMode.noteDiskRead(); return getFileStatusNative(path, status); @@ -90,6 +96,10 @@ public class FileUtils { public static native int setPermissions(String file, int mode, int uid, int gid); + /** + * @deprecated use {@link Os#stat(String)} instead. + */ + @Deprecated public static native int getPermissions(String file, int[] outPermissions); public static native int setUMask(int mask); diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index f4d7af96192b..10edc0678d05 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -81,8 +81,8 @@ public final class Choreographer { private static final int MSG_DO_ANIMATION = 0; private static final int MSG_DO_DRAW = 1; private static final int MSG_DO_SCHEDULE_VSYNC = 2; - private static final int MSG_POST_DELAYED_ANIMATION = 3; - private static final int MSG_POST_DELAYED_DRAW = 4; + private static final int MSG_DO_SCHEDULE_ANIMATION = 3; + private static final int MSG_DO_SCHEDULE_DRAW = 4; private final Object mLock = new Object(); @@ -152,134 +152,158 @@ public final class Choreographer { } /** + * Subtracts typical frame delay time from a delay interval in milliseconds. + * + * This method can be used to compensate for animation delay times that have baked + * in assumptions about the frame delay. For example, it's quite common for code to + * assume a 60Hz frame time and bake in a 16ms delay. When we call + * {@link #postAnimationCallbackDelayed} we want to know how long to wait before + * posting the animation callback but let the animation timer take care of the remaining + * frame delay time. + * + * This method is somewhat conservative about how much of the frame delay it + * subtracts. It uses the same value returned by {@link #getFrameDelay} which by + * default is 10ms even though many parts of the system assume 16ms. Consequently, + * we might still wait 6ms before posting an animation callback that we want to run + * on the next frame, but this is much better than waiting a whole 16ms and likely + * missing the deadline. + * + * @param delayMillis The original delay time including an assumed frame delay. + * @return The adjusted delay time with the assumed frame delay subtracted out. + */ + public static long subtractFrameDelay(long delayMillis) { + final long frameDelay = sFrameDelay; + return delayMillis <= frameDelay ? 0 : delayMillis - frameDelay; + } + + /** * Posts a callback to run on the next animation cycle. * The callback only runs once and then is automatically removed. * - * @param runnable The callback to run during the next animation cycle. + * @param action The callback action to run during the next animation cycle. + * @param token The callback token, or null if none. * * @see #removeAnimationCallback */ - public void postAnimationCallback(Runnable runnable) { - if (runnable == null) { - throw new IllegalArgumentException("runnable must not be null"); - } - postAnimationCallbackUnchecked(runnable); - } - - private void postAnimationCallbackUnchecked(Runnable runnable) { - synchronized (mLock) { - mAnimationCallbacks = addCallbackLocked(mAnimationCallbacks, runnable); - scheduleAnimationLocked(); - } + public void postAnimationCallback(Runnable action, Object token) { + postAnimationCallbackDelayed(action, token, 0); } /** * Posts a callback to run on the next animation cycle following the specified delay. * The callback only runs once and then is automatically removed. * - * @param runnable The callback to run during the next animation cycle following + * @param action The callback action to run during the next animation cycle after * the specified delay. + * @param token The callback token, or null if none. * @param delayMillis The delay time in milliseconds. * * @see #removeAnimationCallback */ - public void postAnimationCallbackDelayed(Runnable runnable, long delayMillis) { - if (runnable == null) { - throw new IllegalArgumentException("runnable must not be null"); + public void postAnimationCallbackDelayed(Runnable action, Object token, long delayMillis) { + if (action == null) { + throw new IllegalArgumentException("action must not be null"); } - if (delayMillis <= 0) { - postAnimationCallbackUnchecked(runnable); - } else { - Message msg = mHandler.obtainMessage(MSG_POST_DELAYED_ANIMATION, runnable); - mHandler.sendMessageDelayed(msg, delayMillis); + + synchronized (mLock) { + final long now = SystemClock.uptimeMillis(); + final long dueTime = now + delayMillis; + mAnimationCallbacks = addCallbackLocked(mAnimationCallbacks, dueTime, action, token); + + if (dueTime <= now) { + scheduleAnimationLocked(now); + } else { + Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_ANIMATION, action); + mHandler.sendMessageAtTime(msg, dueTime); + } } } /** - * Removes animation callbacks for the specified runnable. - * Does nothing if the specified animation callback has not been posted or has already - * been removed. + * Removes animation callbacks that have the specified action and token. * - * @param runnable The animation callback to remove. + * @param action The action property of the callbacks to remove, or null to remove + * callbacks with any action. + * @param token The token property of the callbacks to remove, or null to remove + * callbacks with any token. * * @see #postAnimationCallback * @see #postAnimationCallbackDelayed */ - public void removeAnimationCallbacks(Runnable runnable) { - if (runnable == null) { - throw new IllegalArgumentException("runnable must not be null"); - } + public void removeAnimationCallbacks(Runnable action, Object token) { synchronized (mLock) { - mAnimationCallbacks = removeCallbacksLocked(mAnimationCallbacks, runnable); + mAnimationCallbacks = removeCallbacksLocked(mAnimationCallbacks, action, token); + if (action != null && token == null) { + mHandler.removeMessages(MSG_DO_SCHEDULE_ANIMATION, action); + } } - mHandler.removeMessages(MSG_POST_DELAYED_ANIMATION, runnable); } /** * Posts a callback to run on the next draw cycle. * The callback only runs once and then is automatically removed. * - * @param runnable The callback to run during the next draw cycle. + * @param action The callback action to run during the next draw cycle. + * @param token The callback token, or null if none. * * @see #removeDrawCallback */ - public void postDrawCallback(Runnable runnable) { - if (runnable == null) { - throw new IllegalArgumentException("runnable must not be null"); - } - postDrawCallbackUnchecked(runnable); - } - - private void postDrawCallbackUnchecked(Runnable runnable) { - synchronized (mLock) { - mDrawCallbacks = addCallbackLocked(mDrawCallbacks, runnable); - scheduleDrawLocked(); - } + public void postDrawCallback(Runnable action, Object token) { + postDrawCallbackDelayed(action, token, 0); } /** * Posts a callback to run on the next draw cycle following the specified delay. * The callback only runs once and then is automatically removed. * - * @param runnable The callback to run during the next draw cycle following + * @param action The callback action to run during the next animation cycle after * the specified delay. + * @param token The callback token, or null if none. * @param delayMillis The delay time in milliseconds. * * @see #removeDrawCallback */ - public void postDrawCallbackDelayed(Runnable runnable, long delayMillis) { - if (runnable == null) { - throw new IllegalArgumentException("runnable must not be null"); + public void postDrawCallbackDelayed(Runnable action, Object token, long delayMillis) { + if (action == null) { + throw new IllegalArgumentException("action must not be null"); } - if (delayMillis <= 0) { - postDrawCallbackUnchecked(runnable); - } else { - Message msg = mHandler.obtainMessage(MSG_POST_DELAYED_DRAW, runnable); - mHandler.sendMessageDelayed(msg, delayMillis); + + synchronized (mLock) { + final long now = SystemClock.uptimeMillis(); + final long dueTime = now + delayMillis; + mDrawCallbacks = addCallbackLocked(mDrawCallbacks, dueTime, action, token); + scheduleDrawLocked(now); + + if (dueTime <= now) { + scheduleDrawLocked(now); + } else { + Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_DRAW, action); + mHandler.sendMessageAtTime(msg, dueTime); + } } } /** - * Removes draw callbacks for the specified runnable. - * Does nothing if the specified draw callback has not been posted or has already - * been removed. + * Removes draw callbacks that have the specified action and token. * - * @param runnable The draw callback to remove. + * @param action The action property of the callbacks to remove, or null to remove + * callbacks with any action. + * @param token The token property of the callbacks to remove, or null to remove + * callbacks with any token. * * @see #postDrawCallback * @see #postDrawCallbackDelayed */ - public void removeDrawCallbacks(Runnable runnable) { - if (runnable == null) { - throw new IllegalArgumentException("runnable must not be null"); - } + public void removeDrawCallbacks(Runnable action, Object token) { synchronized (mLock) { - mDrawCallbacks = removeCallbacksLocked(mDrawCallbacks, runnable); + mDrawCallbacks = removeCallbacksLocked(mDrawCallbacks, action, token); + if (action != null && token == null) { + mHandler.removeMessages(MSG_DO_SCHEDULE_DRAW, action); + } } - mHandler.removeMessages(MSG_POST_DELAYED_DRAW, runnable); } - private void scheduleAnimationLocked() { + private void scheduleAnimationLocked(long now) { if (!mAnimationScheduled) { mAnimationScheduled = true; if (USE_VSYNC) { @@ -291,14 +315,13 @@ public final class Choreographer { // otherwise post a message to schedule the vsync from the UI thread // as soon as possible. if (isRunningOnLooperThreadLocked()) { - doScheduleVsyncLocked(); + scheduleVsyncLocked(); } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); msg.setAsynchronous(true); mHandler.sendMessageAtFrontOfQueue(msg); } } else { - final long now = SystemClock.uptimeMillis(); final long nextAnimationTime = Math.max(mLastAnimationTime + sFrameDelay, now); if (DEBUG) { Log.d(TAG, "Scheduling animation in " + (nextAnimationTime - now) + " ms."); @@ -310,18 +333,18 @@ public final class Choreographer { } } - private void scheduleDrawLocked() { + private void scheduleDrawLocked(long now) { if (!mDrawScheduled) { mDrawScheduled = true; if (USE_ANIMATION_TIMER_FOR_DRAW) { - scheduleAnimationLocked(); + scheduleAnimationLocked(now); } else { if (DEBUG) { Log.d(TAG, "Scheduling draw immediately."); } Message msg = mHandler.obtainMessage(MSG_DO_DRAW); msg.setAsynchronous(true); - mHandler.sendMessage(msg); + mHandler.sendMessageAtTime(msg, now); } } } @@ -336,7 +359,7 @@ public final class Choreographer { void doAnimationInner() { final long start; - final Callback callbacks; + Callback callbacks; synchronized (mLock) { if (!mAnimationScheduled) { return; // no work to do @@ -351,7 +374,23 @@ public final class Choreographer { mLastAnimationTime = start; callbacks = mAnimationCallbacks; - mAnimationCallbacks = null; + if (callbacks != null) { + if (callbacks.dueTime > start) { + callbacks = null; + } else { + Callback predecessor = callbacks; + Callback successor = predecessor.next; + while (successor != null) { + if (successor.dueTime > start) { + predecessor.next = null; + break; + } + predecessor = successor; + successor = successor.next; + } + mAnimationCallbacks = successor; + } + } } if (callbacks != null) { @@ -368,7 +407,7 @@ public final class Choreographer { void doDraw() { final long start; - final Callback callbacks; + Callback callbacks; synchronized (mLock) { if (!mDrawScheduled) { return; // no work to do @@ -383,7 +422,23 @@ public final class Choreographer { mLastDrawTime = start; callbacks = mDrawCallbacks; - mDrawCallbacks = null; + if (callbacks != null) { + if (callbacks.dueTime > start) { + callbacks = null; + } else { + Callback predecessor = callbacks; + Callback successor = predecessor.next; + while (successor != null) { + if (successor.dueTime > start) { + predecessor.next = null; + break; + } + predecessor = successor; + successor = successor.next; + } + mDrawCallbacks = successor; + } + } } if (callbacks != null) { @@ -400,38 +455,66 @@ public final class Choreographer { void doScheduleVsync() { synchronized (mLock) { - doScheduleVsyncLocked(); + if (mAnimationScheduled) { + scheduleVsyncLocked(); + } } } - private void doScheduleVsyncLocked() { - if (mAnimationScheduled) { - mDisplayEventReceiver.scheduleVsync(); + void doScheduleAnimation() { + synchronized (mLock) { + final long now = SystemClock.uptimeMillis(); + if (mAnimationCallbacks != null && mAnimationCallbacks.dueTime <= now) { + scheduleAnimationLocked(now); + } } } + void doScheduleDraw() { + synchronized (mLock) { + final long now = SystemClock.uptimeMillis(); + if (mDrawCallbacks != null && mDrawCallbacks.dueTime <= now) { + scheduleDrawLocked(now); + } + } + } + + private void scheduleVsyncLocked() { + mDisplayEventReceiver.scheduleVsync(); + } + private boolean isRunningOnLooperThreadLocked() { return Looper.myLooper() == mLooper; } - private Callback addCallbackLocked(Callback head, Runnable runnable) { - Callback callback = obtainCallbackLocked(runnable); + private Callback addCallbackLocked(Callback head, + long dueTime, Runnable action, Object token) { + Callback callback = obtainCallbackLocked(dueTime, action, token); if (head == null) { return callback; } - Callback tail = head; - while (tail.next != null) { - tail = tail.next; + Callback entry = head; + if (dueTime < entry.dueTime) { + callback.next = entry; + return callback; + } + while (entry.next != null) { + if (dueTime < entry.next.dueTime) { + callback.next = entry.next; + break; + } + entry = entry.next; } - tail.next = callback; + entry.next = callback; return head; } - private Callback removeCallbacksLocked(Callback head, Runnable runnable) { + private Callback removeCallbacksLocked(Callback head, Runnable action, Object token) { Callback predecessor = null; for (Callback callback = head; callback != null;) { final Callback next = callback.next; - if (callback.runnable == runnable) { + if ((action == null || callback.action == action) + && (token == null || callback.token == token)) { if (predecessor != null) { predecessor.next = next; } else { @@ -448,7 +531,7 @@ public final class Choreographer { private void runCallbacks(Callback head) { while (head != null) { - head.runnable.run(); + head.action.run(); head = head.next; } } @@ -461,7 +544,7 @@ public final class Choreographer { } } - private Callback obtainCallbackLocked(Runnable runnable) { + private Callback obtainCallbackLocked(long dueTime, Runnable action, Object token) { Callback callback = mCallbackPool; if (callback == null) { callback = new Callback(); @@ -469,12 +552,15 @@ public final class Choreographer { mCallbackPool = callback.next; callback.next = null; } - callback.runnable = runnable; + callback.dueTime = dueTime; + callback.action = action; + callback.token = token; return callback; } private void recycleCallbackLocked(Callback callback) { - callback.runnable = null; + callback.action = null; + callback.token = null; callback.next = mCallbackPool; mCallbackPool = callback; } @@ -496,11 +582,11 @@ public final class Choreographer { case MSG_DO_SCHEDULE_VSYNC: doScheduleVsync(); break; - case MSG_POST_DELAYED_ANIMATION: - postAnimationCallbackUnchecked((Runnable)msg.obj); + case MSG_DO_SCHEDULE_ANIMATION: + doScheduleAnimation(); break; - case MSG_POST_DELAYED_DRAW: - postDrawCallbackUnchecked((Runnable)msg.obj); + case MSG_DO_SCHEDULE_DRAW: + doScheduleDraw(); break; } } @@ -519,6 +605,8 @@ public final class Choreographer { private static final class Callback { public Callback next; - public Runnable runnable; + public long dueTime; + public Runnable action; + public Object token; } } diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index acb13877c7a4..497bc90b8ed6 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -49,7 +49,7 @@ oneway interface IWindow { boolean reportDraw, in Configuration newConfig); void dispatchAppVisibility(boolean visible); void dispatchGetNewSurface(); - void dispatchScreenStatus(boolean on); + void dispatchScreenState(boolean on); /** * Tell the window that it is either gaining or losing focus. Keep it up diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 94531c8eb31d..a651362d82b6 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1994,6 +1994,20 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal public static final int FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS = 0x00000004; /** + * Indicates that the screen has changed state and is now off. + * + * @see #onScreenStateChanged(int) + */ + public static final int SCREEN_STATE_OFF = 0x0; + + /** + * Indicates that the screen has changed state and is now on. + * + * @see #onScreenStateChanged(int) + */ + public static final int SCREEN_STATE_ON = 0x1; + + /** * Controls the over-scroll mode for this view. * See {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)}, * {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}, @@ -8779,6 +8793,52 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } /** + * <p>Causes the Runnable to execute on the next animation time step. + * The runnable will be run on the user interface thread.</p> + * + * <p>This method can be invoked from outside of the UI thread + * only when this View is attached to a window.</p> + * + * @param action The Runnable that will be executed. + * + * @hide + */ + public void postOnAnimation(Runnable action) { + final AttachInfo attachInfo = mAttachInfo; + if (attachInfo != null) { + attachInfo.mViewRootImpl.mChoreographer.postAnimationCallback(action, null); + } else { + // Assume that post will succeed later + ViewRootImpl.getRunQueue().post(action); + } + } + + /** + * <p>Causes the Runnable to execute on the next animation time step, + * after the specified amount of time elapses. + * The runnable will be run on the user interface thread.</p> + * + * <p>This method can be invoked from outside of the UI thread + * only when this View is attached to a window.</p> + * + * @param action The Runnable that will be executed. + * @param delayMillis The delay (in milliseconds) until the Runnable + * will be executed. + * + * @hide + */ + public void postOnAnimationDelayed(Runnable action, long delayMillis) { + final AttachInfo attachInfo = mAttachInfo; + if (attachInfo != null) { + attachInfo.mViewRootImpl.mChoreographer.postAnimationCallbackDelayed( + action, null, delayMillis); + } else { + // Assume that post will succeed later + ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); + } + } + + /** * <p>Removes the specified Runnable from the message queue.</p> * * <p>This method can be invoked from outside of the UI thread @@ -8795,6 +8855,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { attachInfo.mHandler.removeCallbacks(action); + attachInfo.mViewRootImpl.mChoreographer.removeAnimationCallbacks(action, null); } else { // Assume that post will succeed later ViewRootImpl.getRunQueue().removeCallbacks(action); @@ -9604,6 +9665,25 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } /** + * @see #onScreenStateChanged(int) + */ + void dispatchScreenStateChanged(int screenState) { + onScreenStateChanged(screenState); + } + + /** + * This method is called whenever the state of the screen this view is + * attached to changes. A state change will usually occurs when the screen + * turns on or off (whether it happens automatically or the user does it + * manually.) + * + * @param screenState The new state of the screen. Can be either + * {@link #SCREEN_STATE_ON} or {@link #SCREEN_STATE_OFF} + */ + public void onScreenStateChanged(int screenState) { + } + + /** * Resolve and cache the layout direction. LTR is set initially. This is implicitly supposing * that the parent directionality can and will be resolved before its children. */ @@ -11847,10 +11927,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal */ public void scheduleDrawable(Drawable who, Runnable what, long when) { if (verifyDrawable(who) && what != null) { + final long delay = when - SystemClock.uptimeMillis(); if (mAttachInfo != null) { - mAttachInfo.mHandler.postAtTime(what, who, when); + mAttachInfo.mViewRootImpl.mChoreographer.postAnimationCallbackDelayed( + what, who, Choreographer.subtractFrameDelay(delay)); } else { - ViewRootImpl.getRunQueue().postDelayed(what, when - SystemClock.uptimeMillis()); + ViewRootImpl.getRunQueue().postDelayed(what, delay); } } } @@ -11864,7 +11946,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal public void unscheduleDrawable(Drawable who, Runnable what) { if (verifyDrawable(who) && what != null) { if (mAttachInfo != null) { - mAttachInfo.mHandler.removeCallbacks(what, who); + mAttachInfo.mViewRootImpl.mChoreographer.removeAnimationCallbacks(what, who); } else { ViewRootImpl.getRunQueue().removeCallbacks(what); } @@ -11882,7 +11964,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal */ public void unscheduleDrawable(Drawable who) { if (mAttachInfo != null) { - mAttachInfo.mHandler.removeCallbacksAndMessages(who); + mAttachInfo.mViewRootImpl.mChoreographer.removeAnimationCallbacks(null, who); } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 0c63286d9ad3..c9e02420becb 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2254,6 +2254,17 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override + void dispatchScreenStateChanged(int screenState) { + super.dispatchScreenStateChanged(screenState); + + final int count = mChildrenCount; + final View[] children = mChildren; + for (int i = 0; i < count; i++) { + children[i].dispatchScreenStateChanged(screenState); + } + } + + @Override boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { boolean handled = super.dispatchPopulateAccessibilityEventInternal(event); if (handled) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7fd05c3d0c97..72365c795ec8 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -761,9 +761,12 @@ public final class ViewRootImpl implements ViewParent, scheduleTraversals(); } - void handleScreenStatusChange(boolean on) { + void handleScreenStateChange(boolean on) { if (on != mAttachInfo.mScreenOn) { mAttachInfo.mScreenOn = on; + if (mView != null) { + mView.dispatchScreenStateChanged(on ? View.SCREEN_STATE_ON : View.SCREEN_STATE_OFF); + } if (on) { mFullRedrawNeeded = true; scheduleTraversals(); @@ -881,7 +884,7 @@ public final class ViewRootImpl implements ViewParent, void scheduleFrame() { if (!mFrameScheduled) { mFrameScheduled = true; - mChoreographer.postDrawCallback(mFrameRunnable); + mChoreographer.postDrawCallback(mFrameRunnable, null); } } @@ -890,7 +893,7 @@ public final class ViewRootImpl implements ViewParent, if (mFrameScheduled) { mFrameScheduled = false; - mChoreographer.removeDrawCallbacks(mFrameRunnable); + mChoreographer.removeDrawCallbacks(mFrameRunnable, null); } } @@ -2500,7 +2503,7 @@ public final class ViewRootImpl implements ViewParent, private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 21; private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 22; private final static int MSG_PROCESS_INPUT_EVENTS = 23; - private final static int MSG_DISPATCH_SCREEN_STATUS = 24; + private final static int MSG_DISPATCH_SCREEN_STATE = 24; final class ViewRootHandler extends Handler { @Override @@ -2757,9 +2760,9 @@ public final class ViewRootImpl implements ViewParent, .findAccessibilityNodeInfosByTextUiThread(msg); } } break; - case MSG_DISPATCH_SCREEN_STATUS: { + case MSG_DISPATCH_SCREEN_STATE: { if (mView != null) { - handleScreenStatusChange(msg.arg1 == 1); + handleScreenStateChange(msg.arg1 == 1); } } break; } @@ -4048,7 +4051,7 @@ public final class ViewRootImpl implements ViewParent, } if (mPosted && mViews.isEmpty() && mViewRects.isEmpty()) { - mChoreographer.removeAnimationCallbacks(this); + mChoreographer.removeAnimationCallbacks(this, null); mPosted = false; } } @@ -4089,7 +4092,7 @@ public final class ViewRootImpl implements ViewParent, private void postIfNeededLocked() { if (!mPosted) { - mChoreographer.postAnimationCallback(this); + mChoreographer.postAnimationCallback(this, null); mPosted = true; } } @@ -4142,8 +4145,8 @@ public final class ViewRootImpl implements ViewParent, mHandler.sendMessage(msg); } - public void dispatchScreenStatusChange(boolean on) { - Message msg = mHandler.obtainMessage(MSG_DISPATCH_SCREEN_STATUS); + public void dispatchScreenStateChange(boolean on) { + Message msg = mHandler.obtainMessage(MSG_DISPATCH_SCREEN_STATE); msg.arg1 = on ? 1 : 0; mHandler.sendMessage(msg); } @@ -4349,13 +4352,13 @@ public final class ViewRootImpl implements ViewParent, } } - public void dispatchScreenStatus(boolean on) { + public void dispatchScreenState(boolean on) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { - viewAncestor.dispatchScreenStatusChange(on); + viewAncestor.dispatchScreenStateChange(on); } } - + public void dispatchGetNewSurface() { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index 334b9c450ef2..23e55e2be2fa 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -169,6 +169,8 @@ public class Switch extends CompoundButton { /** * Sets the switch text color, size, style, hint color, and highlight color * from the specified TextAppearance resource. + * + * @attr ref android.R.styleable#Switch_switchTextAppearance */ public void setSwitchTextAppearance(Context context, int resid) { TypedArray appearance = @@ -274,7 +276,128 @@ public class Switch extends CompoundButton { } /** + * Set the amount of horizontal padding between the switch and the associated text. + * + * @param pixels Amount of padding in pixels + * + * @attr ref android.R.styleable#Switch_switchPadding + */ + public void setSwitchPadding(int pixels) { + mSwitchPadding = pixels; + requestLayout(); + } + + /** + * Get the amount of horizontal padding between the switch and the associated text. + * + * @return Amount of padding in pixels + * + * @attr ref android.R.styleable#Switch_switchPadding + */ + public int getSwitchPadding() { + return mSwitchPadding; + } + + /** + * Set the minimum width of the switch in pixels. The switch's width will be the maximum + * of this value and its measured width as determined by the switch drawables and text used. + * + * @param pixels Minimum width of the switch in pixels + * + * @attr ref android.R.styleable#Switch_switchMinWidth + */ + public void setSwitchMinWidth(int pixels) { + mSwitchMinWidth = pixels; + requestLayout(); + } + + /** + * Get the minimum width of the switch in pixels. The switch's width will be the maximum + * of this value and its measured width as determined by the switch drawables and text used. + * + * @return Minimum width of the switch in pixels + * + * @attr ref android.R.styleable#Switch_switchMinWidth + */ + public int getSwitchMinWidth() { + return mSwitchMinWidth; + } + + /** + * Set the horizontal padding around the text drawn on the switch itself. + * + * @param pixels Horizontal padding for switch thumb text in pixels + * + * @attr ref android.R.styleable#Switch_thumbTextPadding + */ + public void setThumbTextPadding(int pixels) { + mThumbTextPadding = pixels; + requestLayout(); + } + + /** + * Get the horizontal padding around the text drawn on the switch itself. + * + * @return Horizontal padding for switch thumb text in pixels + * + * @attr ref android.R.styleable#Switch_thumbTextPadding + */ + public int getThumbTextPadding() { + return mThumbTextPadding; + } + + /** + * Set the drawable used for the track that the switch slides within. + * + * @param track Track drawable + * + * @attr ref android.R.styleable#Switch_track + */ + public void setTrackDrawable(Drawable track) { + mTrackDrawable = track; + requestLayout(); + } + + /** + * Get the drawable used for the track that the switch slides within. + * + * @return Track drawable + * + * @attr ref android.R.styleable#Switch_track + */ + public Drawable getTrackDrawable() { + return mTrackDrawable; + } + + /** + * Set the drawable used for the switch "thumb" - the piece that the user + * can physically touch and drag along the track. + * + * @param thumb Thumb drawable + * + * @attr ref android.R.styleable#Switch_thumb + */ + public void setThumbDrawable(Drawable thumb) { + mThumbDrawable = thumb; + requestLayout(); + } + + /** + * Get the drawable used for the switch "thumb" - the piece that the user + * can physically touch and drag along the track. + * + * @return Thumb drawable + * + * @attr ref android.R.styleable#Switch_thumb + */ + public Drawable getThumbDrawable() { + return mThumbDrawable; + } + + /** * Returns the text displayed when the button is in the checked state. + * + * @attr ref android.R.styleable#Switch_textOn */ public CharSequence getTextOn() { return mTextOn; @@ -282,6 +405,8 @@ public class Switch extends CompoundButton { /** * Sets the text displayed when the button is in the checked state. + * + * @attr ref android.R.styleable#Switch_textOn */ public void setTextOn(CharSequence textOn) { mTextOn = textOn; @@ -290,6 +415,8 @@ public class Switch extends CompoundButton { /** * Returns the text displayed when the button is not in the checked state. + * + * @attr ref android.R.styleable#Switch_textOff */ public CharSequence getTextOff() { return mTextOff; @@ -297,6 +424,8 @@ public class Switch extends CompoundButton { /** * Sets the text displayed when the button is not in the checked state. + * + * @attr ref android.R.styleable#Switch_textOff */ public void setTextOff(CharSequence textOff) { mTextOff = textOff; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 5ee739290c0d..4c89218d64c3 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -4280,6 +4280,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override + public void onScreenStateChanged(int screenState) { + super.onScreenStateChanged(screenState); + if (mEditor != null) getEditor().onScreenStateChanged(screenState); + } + + @Override protected boolean isPaddingOffsetRequired() { return mShadowRadius != 0 || mDrawables != null; } @@ -11400,6 +11406,30 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener hideControllers(); } + void onScreenStateChanged(int screenState) { + switch (screenState) { + case SCREEN_STATE_ON: + resumeBlink(); + break; + case SCREEN_STATE_OFF: + suspendBlink(); + break; + } + } + + private void suspendBlink() { + if (mBlink != null) { + mBlink.cancel(); + } + } + + private void resumeBlink() { + if (mBlink != null) { + mBlink.uncancel(); + makeBlink(); + } + } + void adjustInputType(boolean password, boolean passwordInputType, boolean webPasswordInputType, boolean numberPasswordInputType) { // mInputType has been set from inputType, possibly modified by mInputMethod. diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 6a99a2bb3473..998c037fc23a 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -348,6 +348,7 @@ public class ZygoteInit { TypedArray ar = mResources.obtainTypedArray( com.android.internal.R.array.preloaded_drawables); int N = preloadDrawables(runtime, ar); + ar.recycle(); Log.i(TAG, "...preloaded " + N + " resources in " + (SystemClock.uptimeMillis()-startTime) + "ms."); @@ -355,6 +356,7 @@ public class ZygoteInit { ar = mResources.obtainTypedArray( com.android.internal.R.array.preloaded_color_state_lists); N = preloadColorStateLists(runtime, ar); + ar.recycle(); Log.i(TAG, "...preloaded " + N + " resources in " + (SystemClock.uptimeMillis()-startTime) + "ms."); } diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index e695f8efbcb8..15d11d8e26a7 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -49,7 +49,7 @@ public class BaseIWindow extends IWindow.Stub { public void dispatchGetNewSurface() { } - public void dispatchScreenStatus(boolean on) { + public void dispatchScreenState(boolean on) { } public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) { diff --git a/core/jni/Android.mk b/core/jni/Android.mk index c389cf7179cb..642988be8840 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -64,6 +64,7 @@ LOCAL_SRC_FILES:= \ android_os_MemoryFile.cpp \ android_os_MessageQueue.cpp \ android_os_ParcelFileDescriptor.cpp \ + android_os_Parcel.cpp \ android_os_Power.cpp \ android_os_StatFs.cpp \ android_os_SystemClock.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 3067e75d71f1..de9fd33d053b 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -130,6 +130,7 @@ extern int register_android_nio_utils(JNIEnv* env); extern int register_android_text_format_Time(JNIEnv* env); extern int register_android_os_Debug(JNIEnv* env); extern int register_android_os_MessageQueue(JNIEnv* env); +extern int register_android_os_Parcel(JNIEnv* env); extern int register_android_os_ParcelFileDescriptor(JNIEnv *env); extern int register_android_os_Power(JNIEnv *env); extern int register_android_os_StatFs(JNIEnv *env); @@ -1094,6 +1095,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_os_Process), REG_JNI(register_android_os_SystemProperties), REG_JNI(register_android_os_Binder), + REG_JNI(register_android_os_Parcel), REG_JNI(register_android_view_Display), REG_JNI(register_android_view_DisplayEventReceiver), REG_JNI(register_android_nio_utils), diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index d1d3b787c335..5e73a5fc1ea6 100644 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -7,6 +7,7 @@ #include "SkUnPreMultiply.h"
#include <binder/Parcel.h>
+#include "android_os_Parcel.h" #include "android_util_Binder.h"
#include "android_nio_utils.h"
#include "CreateJavaOutputStreamAdaptor.h"
diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp index 5c6ebdf0b9c4..866d223c40b8 100644 --- a/core/jni/android/graphics/Region.cpp +++ b/core/jni/android/graphics/Region.cpp @@ -19,6 +19,7 @@ #include "GraphicsJNI.h" #include <binder/Parcel.h> +#include "android_os_Parcel.h" #include "android_util_Binder.h" #include <jni.h> diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp index 579d6ad858ab..ea02f537c0e7 100644 --- a/core/jni/android_database_CursorWindow.cpp +++ b/core/jni/android_database_CursorWindow.cpp @@ -31,6 +31,7 @@ #include <unistd.h> #include <androidfw/CursorWindow.h> +#include "android_os_Parcel.h" #include "android_util_Binder.h" #include "android_database_SQLiteCommon.h" diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp new file mode 100644 index 000000000000..8a99049b0b42 --- /dev/null +++ b/core/jni/android_os_Parcel.cpp @@ -0,0 +1,701 @@ +/* + * 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. + */ + +#define LOG_TAG "Parcel" +//#define LOG_NDEBUG 0 + +#include "android_os_Parcel.h" +#include "android_util_Binder.h" + +#include "JNIHelp.h" + +#include <fcntl.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <utils/Atomic.h> +#include <binder/IInterface.h> +#include <binder/IPCThreadState.h> +#include <utils/Log.h> +#include <utils/SystemClock.h> +#include <utils/List.h> +#include <utils/KeyedVector.h> +#include <cutils/logger.h> +#include <binder/Parcel.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> +#include <utils/threads.h> +#include <utils/String8.h> + +#include <ScopedUtfChars.h> +#include <ScopedLocalRef.h> + +#include <android_runtime/AndroidRuntime.h> + +//#undef ALOGV +//#define ALOGV(...) fprintf(stderr, __VA_ARGS__) + +#define DEBUG_DEATH 0 +#if DEBUG_DEATH +#define LOGDEATH ALOGD +#else +#define LOGDEATH ALOGV +#endif + +namespace android { + +static struct parcel_offsets_t +{ + jfieldID mObject; + jfieldID mOwnObject; +} gParcelOffsets; + +Parcel* parcelForJavaObject(JNIEnv* env, jobject obj) +{ + if (obj) { + Parcel* p = (Parcel*)env->GetIntField(obj, gParcelOffsets.mObject); + if (p != NULL) { + return p; + } + jniThrowException(env, "java/lang/IllegalStateException", "Parcel has been finalized!"); + } + return NULL; +} + +static jint android_os_Parcel_dataSize(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + return parcel ? parcel->dataSize() : 0; +} + +static jint android_os_Parcel_dataAvail(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + return parcel ? parcel->dataAvail() : 0; +} + +static jint android_os_Parcel_dataPosition(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + return parcel ? parcel->dataPosition() : 0; +} + +static jint android_os_Parcel_dataCapacity(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + return parcel ? parcel->dataCapacity() : 0; +} + +static void android_os_Parcel_setDataSize(JNIEnv* env, jobject clazz, jint size) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + const status_t err = parcel->setDataSize(size); + if (err != NO_ERROR) { + signalExceptionForError(env, clazz, err); + } + } +} + +static void android_os_Parcel_setDataPosition(JNIEnv* env, jobject clazz, jint pos) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + parcel->setDataPosition(pos); + } +} + +static void android_os_Parcel_setDataCapacity(JNIEnv* env, jobject clazz, jint size) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + const status_t err = parcel->setDataCapacity(size); + if (err != NO_ERROR) { + signalExceptionForError(env, clazz, err); + } + } +} + +static jboolean android_os_Parcel_pushAllowFds(JNIEnv* env, jobject clazz, jboolean allowFds) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + jboolean ret = JNI_TRUE; + if (parcel != NULL) { + ret = (jboolean)parcel->pushAllowFds(allowFds); + } + return ret; +} + +static void android_os_Parcel_restoreAllowFds(JNIEnv* env, jobject clazz, jboolean lastValue) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + parcel->restoreAllowFds((bool)lastValue); + } +} + +static void android_os_Parcel_writeNative(JNIEnv* env, jobject clazz, + jobject data, jint offset, + jint length) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel == NULL) { + return; + } + + const status_t err = parcel->writeInt32(length); + if (err != NO_ERROR) { + signalExceptionForError(env, clazz, err); + return; + } + + void* dest = parcel->writeInplace(length); + if (dest == NULL) { + signalExceptionForError(env, clazz, NO_MEMORY); + return; + } + + jbyte* ar = (jbyte*)env->GetPrimitiveArrayCritical((jarray)data, 0); + if (ar) { + memcpy(dest, ar + offset, length); + env->ReleasePrimitiveArrayCritical((jarray)data, ar, 0); + } +} + + +static void android_os_Parcel_writeInt(JNIEnv* env, jobject clazz, jint val) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + const status_t err = parcel->writeInt32(val); + if (err != NO_ERROR) { + signalExceptionForError(env, clazz, err); + } + } +} + +static void android_os_Parcel_writeLong(JNIEnv* env, jobject clazz, jlong val) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + const status_t err = parcel->writeInt64(val); + if (err != NO_ERROR) { + signalExceptionForError(env, clazz, err); + } + } +} + +static void android_os_Parcel_writeFloat(JNIEnv* env, jobject clazz, jfloat val) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + const status_t err = parcel->writeFloat(val); + if (err != NO_ERROR) { + signalExceptionForError(env, clazz, err); + } + } +} + +static void android_os_Parcel_writeDouble(JNIEnv* env, jobject clazz, jdouble val) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + const status_t err = parcel->writeDouble(val); + if (err != NO_ERROR) { + signalExceptionForError(env, clazz, err); + } + } +} + +static void android_os_Parcel_writeString(JNIEnv* env, jobject clazz, jstring val) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + status_t err = NO_MEMORY; + if (val) { + const jchar* str = env->GetStringCritical(val, 0); + if (str) { + err = parcel->writeString16(str, env->GetStringLength(val)); + env->ReleaseStringCritical(val, str); + } + } else { + err = parcel->writeString16(NULL, 0); + } + if (err != NO_ERROR) { + signalExceptionForError(env, clazz, err); + } + } +} + +static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jobject clazz, jobject object) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object)); + if (err != NO_ERROR) { + signalExceptionForError(env, clazz, err); + } + } +} + +static void android_os_Parcel_writeFileDescriptor(JNIEnv* env, jobject clazz, jobject object) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + const status_t err = + parcel->writeDupFileDescriptor(jniGetFDFromFileDescriptor(env, object)); + if (err != NO_ERROR) { + signalExceptionForError(env, clazz, err); + } + } +} + +static jbyteArray android_os_Parcel_createByteArray(JNIEnv* env, jobject clazz) +{ + jbyteArray ret = NULL; + + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + int32_t len = parcel->readInt32(); + + // sanity check the stored length against the true data size + if (len >= 0 && len <= (int32_t)parcel->dataAvail()) { + ret = env->NewByteArray(len); + + if (ret != NULL) { + jbyte* a2 = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0); + if (a2) { + const void* data = parcel->readInplace(len); + memcpy(a2, data, len); + env->ReleasePrimitiveArrayCritical(ret, a2, 0); + } + } + } + } + + return ret; +} + +static jint android_os_Parcel_readInt(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + return parcel->readInt32(); + } + return 0; +} + +static jlong android_os_Parcel_readLong(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + return parcel->readInt64(); + } + return 0; +} + +static jfloat android_os_Parcel_readFloat(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + return parcel->readFloat(); + } + return 0; +} + +static jdouble android_os_Parcel_readDouble(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + return parcel->readDouble(); + } + return 0; +} + +static jstring android_os_Parcel_readString(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + size_t len; + const char16_t* str = parcel->readString16Inplace(&len); + if (str) { + return env->NewString(str, len); + } + return NULL; + } + return NULL; +} + +static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + return javaObjectForIBinder(env, parcel->readStrongBinder()); + } + return NULL; +} + +static jobject android_os_Parcel_readFileDescriptor(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + int fd = parcel->readFileDescriptor(); + if (fd < 0) return NULL; + fd = dup(fd); + if (fd < 0) return NULL; + return jniCreateFileDescriptor(env, fd); + } + return NULL; +} + +static jobject android_os_Parcel_openFileDescriptor(JNIEnv* env, jobject clazz, + jstring name, jint mode) +{ + if (name == NULL) { + jniThrowNullPointerException(env, NULL); + return NULL; + } + const jchar* str = env->GetStringCritical(name, 0); + if (str == NULL) { + // Whatever, whatever. + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return NULL; + } + String8 name8(str, env->GetStringLength(name)); + env->ReleaseStringCritical(name, str); + int flags=0; + switch (mode&0x30000000) { + case 0: + case 0x10000000: + flags = O_RDONLY; + break; + case 0x20000000: + flags = O_WRONLY; + break; + case 0x30000000: + flags = O_RDWR; + break; + } + + if (mode&0x08000000) flags |= O_CREAT; + if (mode&0x04000000) flags |= O_TRUNC; + if (mode&0x02000000) flags |= O_APPEND; + + int realMode = S_IRWXU|S_IRWXG; + if (mode&0x00000001) realMode |= S_IROTH; + if (mode&0x00000002) realMode |= S_IWOTH; + + int fd = open(name8.string(), flags, realMode); + if (fd < 0) { + jniThrowException(env, "java/io/FileNotFoundException", strerror(errno)); + return NULL; + } + jobject object = jniCreateFileDescriptor(env, fd); + if (object == NULL) { + close(fd); + } + return object; +} + +static jobject android_os_Parcel_dupFileDescriptor(JNIEnv* env, jobject clazz, jobject orig) +{ + if (orig == NULL) { + jniThrowNullPointerException(env, NULL); + return NULL; + } + int origfd = jniGetFDFromFileDescriptor(env, orig); + if (origfd < 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", "bad FileDescriptor"); + return NULL; + } + + int fd = dup(origfd); + if (fd < 0) { + jniThrowIOException(env, errno); + return NULL; + } + jobject object = jniCreateFileDescriptor(env, fd); + if (object == NULL) { + close(fd); + } + return object; +} + +static void android_os_Parcel_closeFileDescriptor(JNIEnv* env, jobject clazz, jobject object) +{ + if (object == NULL) { + jniThrowNullPointerException(env, NULL); + return; + } + int fd = jniGetFDFromFileDescriptor(env, object); + if (fd >= 0) { + jniSetFileDescriptorOfFD(env, object, -1); + //ALOGI("Closing ParcelFileDescriptor %d\n", fd); + close(fd); + } +} + +static void android_os_Parcel_clearFileDescriptor(JNIEnv* env, jobject clazz, jobject object) +{ + if (object == NULL) { + jniThrowNullPointerException(env, NULL); + return; + } + int fd = jniGetFDFromFileDescriptor(env, object); + if (fd >= 0) { + jniSetFileDescriptorOfFD(env, object, -1); + } +} + +static void android_os_Parcel_freeBuffer(JNIEnv* env, jobject clazz) +{ + int32_t own = env->GetIntField(clazz, gParcelOffsets.mOwnObject); + if (own) { + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + //ALOGI("Parcel.freeBuffer() called for C++ Parcel %p\n", parcel); + parcel->freeData(); + } + } +} + +static void android_os_Parcel_init(JNIEnv* env, jobject clazz, jint parcelInt) +{ + Parcel* parcel = (Parcel*)parcelInt; + int own = 0; + if (!parcel) { + //ALOGI("Initializing obj %p: creating new Parcel\n", clazz); + own = 1; + parcel = new Parcel; + } else { + //ALOGI("Initializing obj %p: given existing Parcel %p\n", clazz, parcel); + } + if (parcel == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return; + } + //ALOGI("Initializing obj %p from C++ Parcel %p, own=%d\n", clazz, parcel, own); + env->SetIntField(clazz, gParcelOffsets.mOwnObject, own); + env->SetIntField(clazz, gParcelOffsets.mObject, (int)parcel); +} + +static void android_os_Parcel_destroy(JNIEnv* env, jobject clazz) +{ + int32_t own = env->GetIntField(clazz, gParcelOffsets.mOwnObject); + if (own) { + Parcel* parcel = parcelForJavaObject(env, clazz); + env->SetIntField(clazz, gParcelOffsets.mObject, 0); + //ALOGI("Destroying obj %p: deleting C++ Parcel %p\n", clazz, parcel); + delete parcel; + } else { + env->SetIntField(clazz, gParcelOffsets.mObject, 0); + //ALOGI("Destroying obj %p: leaving C++ Parcel %p\n", clazz); + } +} + +static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jobject clazz) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel == NULL) { + return NULL; + } + + // do not marshall if there are binder objects in the parcel + if (parcel->objectsCount()) + { + jniThrowException(env, "java/lang/RuntimeException", "Tried to marshall a Parcel that contained Binder objects."); + return NULL; + } + + jbyteArray ret = env->NewByteArray(parcel->dataSize()); + + if (ret != NULL) + { + jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0); + if (array != NULL) + { + memcpy(array, parcel->data(), parcel->dataSize()); + env->ReleasePrimitiveArrayCritical(ret, array, 0); + } + } + + return ret; +} + +static void android_os_Parcel_unmarshall(JNIEnv* env, jobject clazz, jbyteArray data, jint offset, jint length) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel == NULL || length < 0) { + return; + } + + jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(data, 0); + if (array) + { + parcel->setDataSize(length); + parcel->setDataPosition(0); + + void* raw = parcel->writeInplace(length); + memcpy(raw, (array + offset), length); + + env->ReleasePrimitiveArrayCritical(data, array, 0); + } +} + +static void android_os_Parcel_appendFrom(JNIEnv* env, jobject clazz, jobject parcel, jint offset, jint length) +{ + Parcel* thisParcel = parcelForJavaObject(env, clazz); + if (thisParcel == NULL) { + return; + } + Parcel* otherParcel = parcelForJavaObject(env, parcel); + if (otherParcel == NULL) { + return; + } + + status_t err = thisParcel->appendFrom(otherParcel, offset, length); + if (err != NO_ERROR) { + signalExceptionForError(env, clazz, err); + } +} + +static jboolean android_os_Parcel_hasFileDescriptors(JNIEnv* env, jobject clazz) +{ + jboolean ret = JNI_FALSE; + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + if (parcel->hasFileDescriptors()) { + ret = JNI_TRUE; + } + } + return ret; +} + +static void android_os_Parcel_writeInterfaceToken(JNIEnv* env, jobject clazz, jstring name) +{ + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + // In the current implementation, the token is just the serialized interface name that + // the caller expects to be invoking + const jchar* str = env->GetStringCritical(name, 0); + if (str != NULL) { + parcel->writeInterfaceToken(String16(str, env->GetStringLength(name))); + env->ReleaseStringCritical(name, str); + } + } +} + +static void android_os_Parcel_enforceInterface(JNIEnv* env, jobject clazz, jstring name) +{ + jboolean ret = JNI_FALSE; + + Parcel* parcel = parcelForJavaObject(env, clazz); + if (parcel != NULL) { + const jchar* str = env->GetStringCritical(name, 0); + if (str) { + IPCThreadState* threadState = IPCThreadState::self(); + const int32_t oldPolicy = threadState->getStrictModePolicy(); + const bool isValid = parcel->enforceInterface( + String16(str, env->GetStringLength(name)), + threadState); + env->ReleaseStringCritical(name, str); + if (isValid) { + const int32_t newPolicy = threadState->getStrictModePolicy(); + if (oldPolicy != newPolicy) { + // Need to keep the Java-level thread-local strict + // mode policy in sync for the libcore + // enforcements, which involves an upcall back + // into Java. (We can't modify the + // Parcel.enforceInterface signature, as it's + // pseudo-public, and used via AIDL + // auto-generation...) + set_dalvik_blockguard_policy(env, newPolicy); + } + return; // everything was correct -> return silently + } + } + } + + // all error conditions wind up here + jniThrowException(env, "java/lang/SecurityException", + "Binder invocation to an incorrect interface"); +} + +// ---------------------------------------------------------------------------- + +static const JNINativeMethod gParcelMethods[] = { + {"dataSize", "()I", (void*)android_os_Parcel_dataSize}, + {"dataAvail", "()I", (void*)android_os_Parcel_dataAvail}, + {"dataPosition", "()I", (void*)android_os_Parcel_dataPosition}, + {"dataCapacity", "()I", (void*)android_os_Parcel_dataCapacity}, + {"setDataSize", "(I)V", (void*)android_os_Parcel_setDataSize}, + {"setDataPosition", "(I)V", (void*)android_os_Parcel_setDataPosition}, + {"setDataCapacity", "(I)V", (void*)android_os_Parcel_setDataCapacity}, + {"pushAllowFds", "(Z)Z", (void*)android_os_Parcel_pushAllowFds}, + {"restoreAllowFds", "(Z)V", (void*)android_os_Parcel_restoreAllowFds}, + {"writeNative", "([BII)V", (void*)android_os_Parcel_writeNative}, + {"writeInt", "(I)V", (void*)android_os_Parcel_writeInt}, + {"writeLong", "(J)V", (void*)android_os_Parcel_writeLong}, + {"writeFloat", "(F)V", (void*)android_os_Parcel_writeFloat}, + {"writeDouble", "(D)V", (void*)android_os_Parcel_writeDouble}, + {"writeString", "(Ljava/lang/String;)V", (void*)android_os_Parcel_writeString}, + {"writeStrongBinder", "(Landroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder}, + {"writeFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_writeFileDescriptor}, + {"createByteArray", "()[B", (void*)android_os_Parcel_createByteArray}, + {"readInt", "()I", (void*)android_os_Parcel_readInt}, + {"readLong", "()J", (void*)android_os_Parcel_readLong}, + {"readFloat", "()F", (void*)android_os_Parcel_readFloat}, + {"readDouble", "()D", (void*)android_os_Parcel_readDouble}, + {"readString", "()Ljava/lang/String;", (void*)android_os_Parcel_readString}, + {"readStrongBinder", "()Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder}, + {"internalReadFileDescriptor", "()Ljava/io/FileDescriptor;", (void*)android_os_Parcel_readFileDescriptor}, + {"openFileDescriptor", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_openFileDescriptor}, + {"dupFileDescriptor", "(Ljava/io/FileDescriptor;)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_dupFileDescriptor}, + {"closeFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_closeFileDescriptor}, + {"clearFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_clearFileDescriptor}, + {"freeBuffer", "()V", (void*)android_os_Parcel_freeBuffer}, + {"init", "(I)V", (void*)android_os_Parcel_init}, + {"destroy", "()V", (void*)android_os_Parcel_destroy}, + {"marshall", "()[B", (void*)android_os_Parcel_marshall}, + {"unmarshall", "([BII)V", (void*)android_os_Parcel_unmarshall}, + {"appendFrom", "(Landroid/os/Parcel;II)V", (void*)android_os_Parcel_appendFrom}, + {"hasFileDescriptors", "()Z", (void*)android_os_Parcel_hasFileDescriptors}, + {"writeInterfaceToken", "(Ljava/lang/String;)V", (void*)android_os_Parcel_writeInterfaceToken}, + {"enforceInterface", "(Ljava/lang/String;)V", (void*)android_os_Parcel_enforceInterface}, +}; + +const char* const kParcelPathName = "android/os/Parcel"; + +int register_android_os_Parcel(JNIEnv* env) +{ + jclass clazz; + + clazz = env->FindClass(kParcelPathName); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Parcel"); + + gParcelOffsets.mObject + = env->GetFieldID(clazz, "mObject", "I"); + gParcelOffsets.mOwnObject + = env->GetFieldID(clazz, "mOwnObject", "I"); + + return AndroidRuntime::registerNativeMethods( + env, kParcelPathName, + gParcelMethods, NELEM(gParcelMethods)); +} + +}; diff --git a/core/jni/android_os_Parcel.h b/core/jni/android_os_Parcel.h new file mode 100644 index 000000000000..65f3819ef867 --- /dev/null +++ b/core/jni/android_os_Parcel.h @@ -0,0 +1,27 @@ +/* + * 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. + */ + +#include <binder/IBinder.h> + +#include "jni.h" + +namespace android { + +// Conversion from Java Parcel Object to C++ Parcel instance. +// Note: does not type checking; must guarantee jobject is a Java Parcel +extern Parcel* parcelForJavaObject(JNIEnv* env, jobject obj); + +} diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index e00970a2dbde..0f99fb2af86d 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -17,7 +17,9 @@ #define LOG_TAG "JavaBinder" //#define LOG_NDEBUG 0 +#include "android_os_Parcel.h" #include "android_util_Binder.h" + #include "JNIHelp.h" #include <fcntl.h> @@ -127,12 +129,6 @@ static struct class_offsets_t // ---------------------------------------------------------------------------- -static struct parcel_offsets_t -{ - jfieldID mObject; - jfieldID mOwnObject; -} gParcelOffsets; - static struct log_offsets_t { // Class state. @@ -232,15 +228,6 @@ bail: env->DeleteLocalRef(msgstr); } -static void set_dalvik_blockguard_policy(JNIEnv* env, jint strict_policy) -{ - // Call back into android.os.StrictMode#onBinderStrictModePolicyChange - // to sync our state back to it. See the comments in StrictMode.java. - env->CallStaticVoidMethod(gStrictModeCallbackOffsets.mClass, - gStrictModeCallbackOffsets.mCallback, - strict_policy); -} - class JavaBBinderHolder; class JavaBBinder : public BBinder @@ -634,26 +621,23 @@ sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj) return NULL; } -Parcel* parcelForJavaObject(JNIEnv* env, jobject obj) -{ - if (obj) { - Parcel* p = (Parcel*)env->GetIntField(obj, gParcelOffsets.mObject); - if (p != NULL) { - return p; - } - jniThrowException(env, "java/lang/IllegalStateException", "Parcel has been finalized!"); - } - return NULL; -} - jobject newParcelFileDescriptor(JNIEnv* env, jobject fileDesc) { return env->NewObject( gParcelFileDescriptorOffsets.mClass, gParcelFileDescriptorOffsets.mConstructor, fileDesc); } -static void signalExceptionForError(JNIEnv* env, jobject obj, status_t err, - bool canThrowRemoteException = false) +void set_dalvik_blockguard_policy(JNIEnv* env, jint strict_policy) +{ + // Call back into android.os.StrictMode#onBinderStrictModePolicyChange + // to sync our state back to it. See the comments in StrictMode.java. + env->CallStaticVoidMethod(gStrictModeCallbackOffsets.mClass, + gStrictModeCallbackOffsets.mCallback, + strict_policy); +} + +void signalExceptionForError(JNIEnv* env, jobject obj, status_t err, + bool canThrowRemoteException) { switch (err) { case UNKNOWN_ERROR: @@ -1273,612 +1257,15 @@ static int int_register_android_os_BinderProxy(JNIEnv* env) // **************************************************************************** // **************************************************************************** -static jint android_os_Parcel_dataSize(JNIEnv* env, jobject clazz) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - return parcel ? parcel->dataSize() : 0; -} - -static jint android_os_Parcel_dataAvail(JNIEnv* env, jobject clazz) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - return parcel ? parcel->dataAvail() : 0; -} - -static jint android_os_Parcel_dataPosition(JNIEnv* env, jobject clazz) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - return parcel ? parcel->dataPosition() : 0; -} - -static jint android_os_Parcel_dataCapacity(JNIEnv* env, jobject clazz) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - return parcel ? parcel->dataCapacity() : 0; -} - -static void android_os_Parcel_setDataSize(JNIEnv* env, jobject clazz, jint size) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - const status_t err = parcel->setDataSize(size); - if (err != NO_ERROR) { - signalExceptionForError(env, clazz, err); - } - } -} - -static void android_os_Parcel_setDataPosition(JNIEnv* env, jobject clazz, jint pos) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - parcel->setDataPosition(pos); - } -} - -static void android_os_Parcel_setDataCapacity(JNIEnv* env, jobject clazz, jint size) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - const status_t err = parcel->setDataCapacity(size); - if (err != NO_ERROR) { - signalExceptionForError(env, clazz, err); - } - } -} - -static jboolean android_os_Parcel_pushAllowFds(JNIEnv* env, jobject clazz, jboolean allowFds) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - jboolean ret = JNI_TRUE; - if (parcel != NULL) { - ret = (jboolean)parcel->pushAllowFds(allowFds); - } - return ret; -} - -static void android_os_Parcel_restoreAllowFds(JNIEnv* env, jobject clazz, jboolean lastValue) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - parcel->restoreAllowFds((bool)lastValue); - } -} - -static void android_os_Parcel_writeNative(JNIEnv* env, jobject clazz, - jobject data, jint offset, - jint length) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel == NULL) { - return; - } - - const status_t err = parcel->writeInt32(length); - if (err != NO_ERROR) { - signalExceptionForError(env, clazz, err); - return; - } - - void* dest = parcel->writeInplace(length); - if (dest == NULL) { - signalExceptionForError(env, clazz, NO_MEMORY); - return; - } - - jbyte* ar = (jbyte*)env->GetPrimitiveArrayCritical((jarray)data, 0); - if (ar) { - memcpy(dest, ar + offset, length); - env->ReleasePrimitiveArrayCritical((jarray)data, ar, 0); - } -} - - -static void android_os_Parcel_writeInt(JNIEnv* env, jobject clazz, jint val) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - const status_t err = parcel->writeInt32(val); - if (err != NO_ERROR) { - signalExceptionForError(env, clazz, err); - } - } -} - -static void android_os_Parcel_writeLong(JNIEnv* env, jobject clazz, jlong val) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - const status_t err = parcel->writeInt64(val); - if (err != NO_ERROR) { - signalExceptionForError(env, clazz, err); - } - } -} - -static void android_os_Parcel_writeFloat(JNIEnv* env, jobject clazz, jfloat val) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - const status_t err = parcel->writeFloat(val); - if (err != NO_ERROR) { - signalExceptionForError(env, clazz, err); - } - } -} - -static void android_os_Parcel_writeDouble(JNIEnv* env, jobject clazz, jdouble val) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - const status_t err = parcel->writeDouble(val); - if (err != NO_ERROR) { - signalExceptionForError(env, clazz, err); - } - } -} - -static void android_os_Parcel_writeString(JNIEnv* env, jobject clazz, jstring val) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - status_t err = NO_MEMORY; - if (val) { - const jchar* str = env->GetStringCritical(val, 0); - if (str) { - err = parcel->writeString16(str, env->GetStringLength(val)); - env->ReleaseStringCritical(val, str); - } - } else { - err = parcel->writeString16(NULL, 0); - } - if (err != NO_ERROR) { - signalExceptionForError(env, clazz, err); - } - } -} - -static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jobject clazz, jobject object) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object)); - if (err != NO_ERROR) { - signalExceptionForError(env, clazz, err); - } - } -} - -static void android_os_Parcel_writeFileDescriptor(JNIEnv* env, jobject clazz, jobject object) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - const status_t err = - parcel->writeDupFileDescriptor(jniGetFDFromFileDescriptor(env, object)); - if (err != NO_ERROR) { - signalExceptionForError(env, clazz, err); - } - } -} - -static jbyteArray android_os_Parcel_createByteArray(JNIEnv* env, jobject clazz) -{ - jbyteArray ret = NULL; - - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - int32_t len = parcel->readInt32(); - - // sanity check the stored length against the true data size - if (len >= 0 && len <= (int32_t)parcel->dataAvail()) { - ret = env->NewByteArray(len); - - if (ret != NULL) { - jbyte* a2 = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0); - if (a2) { - const void* data = parcel->readInplace(len); - memcpy(a2, data, len); - env->ReleasePrimitiveArrayCritical(ret, a2, 0); - } - } - } - } - - return ret; -} - -static jint android_os_Parcel_readInt(JNIEnv* env, jobject clazz) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - return parcel->readInt32(); - } - return 0; -} - -static jlong android_os_Parcel_readLong(JNIEnv* env, jobject clazz) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - return parcel->readInt64(); - } - return 0; -} - -static jfloat android_os_Parcel_readFloat(JNIEnv* env, jobject clazz) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - return parcel->readFloat(); - } - return 0; -} - -static jdouble android_os_Parcel_readDouble(JNIEnv* env, jobject clazz) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - return parcel->readDouble(); - } - return 0; -} - -static jstring android_os_Parcel_readString(JNIEnv* env, jobject clazz) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - size_t len; - const char16_t* str = parcel->readString16Inplace(&len); - if (str) { - return env->NewString(str, len); - } - return NULL; - } - return NULL; -} - -static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jobject clazz) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - return javaObjectForIBinder(env, parcel->readStrongBinder()); - } - return NULL; -} - -static jobject android_os_Parcel_readFileDescriptor(JNIEnv* env, jobject clazz) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - int fd = parcel->readFileDescriptor(); - if (fd < 0) return NULL; - fd = dup(fd); - if (fd < 0) return NULL; - return jniCreateFileDescriptor(env, fd); - } - return NULL; -} - -static jobject android_os_Parcel_openFileDescriptor(JNIEnv* env, jobject clazz, - jstring name, jint mode) -{ - if (name == NULL) { - jniThrowNullPointerException(env, NULL); - return NULL; - } - const jchar* str = env->GetStringCritical(name, 0); - if (str == NULL) { - // Whatever, whatever. - jniThrowException(env, "java/lang/IllegalStateException", NULL); - return NULL; - } - String8 name8(str, env->GetStringLength(name)); - env->ReleaseStringCritical(name, str); - int flags=0; - switch (mode&0x30000000) { - case 0: - case 0x10000000: - flags = O_RDONLY; - break; - case 0x20000000: - flags = O_WRONLY; - break; - case 0x30000000: - flags = O_RDWR; - break; - } - - if (mode&0x08000000) flags |= O_CREAT; - if (mode&0x04000000) flags |= O_TRUNC; - if (mode&0x02000000) flags |= O_APPEND; - - int realMode = S_IRWXU|S_IRWXG; - if (mode&0x00000001) realMode |= S_IROTH; - if (mode&0x00000002) realMode |= S_IWOTH; - - int fd = open(name8.string(), flags, realMode); - if (fd < 0) { - jniThrowException(env, "java/io/FileNotFoundException", strerror(errno)); - return NULL; - } - jobject object = jniCreateFileDescriptor(env, fd); - if (object == NULL) { - close(fd); - } - return object; -} - -static jobject android_os_Parcel_dupFileDescriptor(JNIEnv* env, jobject clazz, jobject orig) -{ - if (orig == NULL) { - jniThrowNullPointerException(env, NULL); - return NULL; - } - int origfd = jniGetFDFromFileDescriptor(env, orig); - if (origfd < 0) { - jniThrowException(env, "java/lang/IllegalArgumentException", "bad FileDescriptor"); - return NULL; - } - - int fd = dup(origfd); - if (fd < 0) { - jniThrowIOException(env, errno); - return NULL; - } - jobject object = jniCreateFileDescriptor(env, fd); - if (object == NULL) { - close(fd); - } - return object; -} - -static void android_os_Parcel_closeFileDescriptor(JNIEnv* env, jobject clazz, jobject object) -{ - if (object == NULL) { - jniThrowNullPointerException(env, NULL); - return; - } - int fd = jniGetFDFromFileDescriptor(env, object); - if (fd >= 0) { - jniSetFileDescriptorOfFD(env, object, -1); - //ALOGI("Closing ParcelFileDescriptor %d\n", fd); - close(fd); - } -} - -static void android_os_Parcel_clearFileDescriptor(JNIEnv* env, jobject clazz, jobject object) -{ - if (object == NULL) { - jniThrowNullPointerException(env, NULL); - return; - } - int fd = jniGetFDFromFileDescriptor(env, object); - if (fd >= 0) { - jniSetFileDescriptorOfFD(env, object, -1); - } -} - -static void android_os_Parcel_freeBuffer(JNIEnv* env, jobject clazz) -{ - int32_t own = env->GetIntField(clazz, gParcelOffsets.mOwnObject); - if (own) { - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - //ALOGI("Parcel.freeBuffer() called for C++ Parcel %p\n", parcel); - parcel->freeData(); - } - } -} - -static void android_os_Parcel_init(JNIEnv* env, jobject clazz, jint parcelInt) -{ - Parcel* parcel = (Parcel*)parcelInt; - int own = 0; - if (!parcel) { - //ALOGI("Initializing obj %p: creating new Parcel\n", clazz); - own = 1; - parcel = new Parcel; - } else { - //ALOGI("Initializing obj %p: given existing Parcel %p\n", clazz, parcel); - } - if (parcel == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", NULL); - return; - } - //ALOGI("Initializing obj %p from C++ Parcel %p, own=%d\n", clazz, parcel, own); - env->SetIntField(clazz, gParcelOffsets.mOwnObject, own); - env->SetIntField(clazz, gParcelOffsets.mObject, (int)parcel); -} - -static void android_os_Parcel_destroy(JNIEnv* env, jobject clazz) -{ - int32_t own = env->GetIntField(clazz, gParcelOffsets.mOwnObject); - if (own) { - Parcel* parcel = parcelForJavaObject(env, clazz); - env->SetIntField(clazz, gParcelOffsets.mObject, 0); - //ALOGI("Destroying obj %p: deleting C++ Parcel %p\n", clazz, parcel); - delete parcel; - } else { - env->SetIntField(clazz, gParcelOffsets.mObject, 0); - //ALOGI("Destroying obj %p: leaving C++ Parcel %p\n", clazz); - } -} - -static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jobject clazz) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel == NULL) { - return NULL; - } - - // do not marshall if there are binder objects in the parcel - if (parcel->objectsCount()) - { - jniThrowException(env, "java/lang/RuntimeException", "Tried to marshall a Parcel that contained Binder objects."); - return NULL; - } - - jbyteArray ret = env->NewByteArray(parcel->dataSize()); - - if (ret != NULL) - { - jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0); - if (array != NULL) - { - memcpy(array, parcel->data(), parcel->dataSize()); - env->ReleasePrimitiveArrayCritical(ret, array, 0); - } - } - - return ret; -} - -static void android_os_Parcel_unmarshall(JNIEnv* env, jobject clazz, jbyteArray data, jint offset, jint length) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel == NULL || length < 0) { - return; - } - - jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(data, 0); - if (array) - { - parcel->setDataSize(length); - parcel->setDataPosition(0); - - void* raw = parcel->writeInplace(length); - memcpy(raw, (array + offset), length); - - env->ReleasePrimitiveArrayCritical(data, array, 0); - } -} - -static void android_os_Parcel_appendFrom(JNIEnv* env, jobject clazz, jobject parcel, jint offset, jint length) -{ - Parcel* thisParcel = parcelForJavaObject(env, clazz); - if (thisParcel == NULL) { - return; - } - Parcel* otherParcel = parcelForJavaObject(env, parcel); - if (otherParcel == NULL) { - return; - } - - status_t err = thisParcel->appendFrom(otherParcel, offset, length); - if (err != NO_ERROR) { - signalExceptionForError(env, clazz, err); - } -} - -static jboolean android_os_Parcel_hasFileDescriptors(JNIEnv* env, jobject clazz) -{ - jboolean ret = JNI_FALSE; - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - if (parcel->hasFileDescriptors()) { - ret = JNI_TRUE; - } - } - return ret; -} - -static void android_os_Parcel_writeInterfaceToken(JNIEnv* env, jobject clazz, jstring name) -{ - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - // In the current implementation, the token is just the serialized interface name that - // the caller expects to be invoking - const jchar* str = env->GetStringCritical(name, 0); - if (str != NULL) { - parcel->writeInterfaceToken(String16(str, env->GetStringLength(name))); - env->ReleaseStringCritical(name, str); - } - } -} - -static void android_os_Parcel_enforceInterface(JNIEnv* env, jobject clazz, jstring name) +int register_android_os_Binder(JNIEnv* env) { - jboolean ret = JNI_FALSE; - - Parcel* parcel = parcelForJavaObject(env, clazz); - if (parcel != NULL) { - const jchar* str = env->GetStringCritical(name, 0); - if (str) { - IPCThreadState* threadState = IPCThreadState::self(); - const int32_t oldPolicy = threadState->getStrictModePolicy(); - const bool isValid = parcel->enforceInterface( - String16(str, env->GetStringLength(name)), - threadState); - env->ReleaseStringCritical(name, str); - if (isValid) { - const int32_t newPolicy = threadState->getStrictModePolicy(); - if (oldPolicy != newPolicy) { - // Need to keep the Java-level thread-local strict - // mode policy in sync for the libcore - // enforcements, which involves an upcall back - // into Java. (We can't modify the - // Parcel.enforceInterface signature, as it's - // pseudo-public, and used via AIDL - // auto-generation...) - set_dalvik_blockguard_policy(env, newPolicy); - } - return; // everything was correct -> return silently - } - } - } - - // all error conditions wind up here - jniThrowException(env, "java/lang/SecurityException", - "Binder invocation to an incorrect interface"); -} - -// ---------------------------------------------------------------------------- - -static const JNINativeMethod gParcelMethods[] = { - {"dataSize", "()I", (void*)android_os_Parcel_dataSize}, - {"dataAvail", "()I", (void*)android_os_Parcel_dataAvail}, - {"dataPosition", "()I", (void*)android_os_Parcel_dataPosition}, - {"dataCapacity", "()I", (void*)android_os_Parcel_dataCapacity}, - {"setDataSize", "(I)V", (void*)android_os_Parcel_setDataSize}, - {"setDataPosition", "(I)V", (void*)android_os_Parcel_setDataPosition}, - {"setDataCapacity", "(I)V", (void*)android_os_Parcel_setDataCapacity}, - {"pushAllowFds", "(Z)Z", (void*)android_os_Parcel_pushAllowFds}, - {"restoreAllowFds", "(Z)V", (void*)android_os_Parcel_restoreAllowFds}, - {"writeNative", "([BII)V", (void*)android_os_Parcel_writeNative}, - {"writeInt", "(I)V", (void*)android_os_Parcel_writeInt}, - {"writeLong", "(J)V", (void*)android_os_Parcel_writeLong}, - {"writeFloat", "(F)V", (void*)android_os_Parcel_writeFloat}, - {"writeDouble", "(D)V", (void*)android_os_Parcel_writeDouble}, - {"writeString", "(Ljava/lang/String;)V", (void*)android_os_Parcel_writeString}, - {"writeStrongBinder", "(Landroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder}, - {"writeFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_writeFileDescriptor}, - {"createByteArray", "()[B", (void*)android_os_Parcel_createByteArray}, - {"readInt", "()I", (void*)android_os_Parcel_readInt}, - {"readLong", "()J", (void*)android_os_Parcel_readLong}, - {"readFloat", "()F", (void*)android_os_Parcel_readFloat}, - {"readDouble", "()D", (void*)android_os_Parcel_readDouble}, - {"readString", "()Ljava/lang/String;", (void*)android_os_Parcel_readString}, - {"readStrongBinder", "()Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder}, - {"internalReadFileDescriptor", "()Ljava/io/FileDescriptor;", (void*)android_os_Parcel_readFileDescriptor}, - {"openFileDescriptor", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_openFileDescriptor}, - {"dupFileDescriptor", "(Ljava/io/FileDescriptor;)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_dupFileDescriptor}, - {"closeFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_closeFileDescriptor}, - {"clearFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_clearFileDescriptor}, - {"freeBuffer", "()V", (void*)android_os_Parcel_freeBuffer}, - {"init", "(I)V", (void*)android_os_Parcel_init}, - {"destroy", "()V", (void*)android_os_Parcel_destroy}, - {"marshall", "()[B", (void*)android_os_Parcel_marshall}, - {"unmarshall", "([BII)V", (void*)android_os_Parcel_unmarshall}, - {"appendFrom", "(Landroid/os/Parcel;II)V", (void*)android_os_Parcel_appendFrom}, - {"hasFileDescriptors", "()Z", (void*)android_os_Parcel_hasFileDescriptors}, - {"writeInterfaceToken", "(Ljava/lang/String;)V", (void*)android_os_Parcel_writeInterfaceToken}, - {"enforceInterface", "(Ljava/lang/String;)V", (void*)android_os_Parcel_enforceInterface}, -}; - -const char* const kParcelPathName = "android/os/Parcel"; + if (int_register_android_os_Binder(env) < 0) + return -1; + if (int_register_android_os_BinderInternal(env) < 0) + return -1; + if (int_register_android_os_BinderProxy(env) < 0) + return -1; -static int int_register_android_os_Parcel(JNIEnv* env) -{ jclass clazz; clazz = env->FindClass("android/util/Log"); @@ -1894,14 +1281,6 @@ static int int_register_android_os_Parcel(JNIEnv* env) gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V"); - clazz = env->FindClass(kParcelPathName); - LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Parcel"); - - gParcelOffsets.mObject - = env->GetFieldID(clazz, "mObject", "I"); - gParcelOffsets.mOwnObject - = env->GetFieldID(clazz, "mOwnObject", "I"); - clazz = env->FindClass("android/os/StrictMode"); LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.StrictMode"); gStrictModeCallbackOffsets.mClass = (jclass) env->NewGlobalRef(clazz); @@ -1910,20 +1289,5 @@ static int int_register_android_os_Parcel(JNIEnv* env) LOG_FATAL_IF(gStrictModeCallbackOffsets.mCallback == NULL, "Unable to find strict mode callback."); - return AndroidRuntime::registerNativeMethods( - env, kParcelPathName, - gParcelMethods, NELEM(gParcelMethods)); -} - -int register_android_os_Binder(JNIEnv* env) -{ - if (int_register_android_os_Binder(env) < 0) - return -1; - if (int_register_android_os_BinderInternal(env) < 0) - return -1; - if (int_register_android_os_BinderProxy(env) < 0) - return -1; - if (int_register_android_os_Parcel(env) < 0) - return -1; return 0; } diff --git a/core/jni/android_util_Binder.h b/core/jni/android_util_Binder.h index 0122691b25da..ca320effd69e 100644 --- a/core/jni/android_util_Binder.h +++ b/core/jni/android_util_Binder.h @@ -15,6 +15,9 @@ ** limitations under the License. */ +#ifndef ANDROID_UTIL_BINDER_H +#define ANDROID_UTIL_BINDER_H + #include <binder/IBinder.h> #include "jni.h" @@ -25,10 +28,13 @@ namespace android { extern jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val); extern sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj); -// Conversion from Java Parcel Object to C++ Parcel instance. -// Note: does not type checking; must guarantee jobject is a Java Parcel -extern Parcel* parcelForJavaObject(JNIEnv* env, jobject obj); - extern jobject newParcelFileDescriptor(JNIEnv* env, jobject fileDesc); +extern void set_dalvik_blockguard_policy(JNIEnv* env, jint strict_policy); + +extern void signalExceptionForError(JNIEnv* env, jobject obj, status_t err, + bool canThrowRemoteException = false); + } + +#endif diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp index 8350e73f91dc..9c44a599a7a8 100644 --- a/core/jni/android_view_InputChannel.cpp +++ b/core/jni/android_view_InputChannel.cpp @@ -23,6 +23,7 @@ #include <utils/Log.h> #include <androidfw/InputTransport.h> #include "android_view_InputChannel.h" +#include "android_os_Parcel.h" #include "android_util_Binder.h" namespace android { diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp index 0fb1b17a2b8e..e69fb74ecf16 100644 --- a/core/jni/android_view_MotionEvent.cpp +++ b/core/jni/android_view_MotionEvent.cpp @@ -21,6 +21,7 @@ #include <android_runtime/AndroidRuntime.h> #include <utils/Log.h> #include <androidfw/Input.h> +#include "android_os_Parcel.h" #include "android_view_MotionEvent.h" #include "android_util_Binder.h" #include "android/graphics/Matrix.h" diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 1a0d47978c0a..8e36948b6a2e 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -671,7 +671,7 @@ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Se superó el máximo de intentos permitido para el desbloqueo facial del dispositivo."</string> <string name="lockscreen_plugged_in" msgid="8057762828355572315">"Cargando <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string> <string name="lockscreen_charged" msgid="4938930459620989972">"Cargada."</string> - <string name="lockscreen_battery_short" msgid="4477264849386850266">"<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string> + <string name="lockscreen_battery_short" msgid="4477264849386850266">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string> <string name="lockscreen_low_battery" msgid="1482873981919249740">"Conecta tu cargador."</string> <string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"No hay tarjeta SIM."</string> <string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"No hay tarjeta SIM en el tablet."</string> @@ -768,8 +768,8 @@ <string name="permdesc_serialPort" msgid="2991639985224598193">"Permite acceder a puertos serie a través de la API SerialManager."</string> <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"acceder a proveedores externamente"</string> <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permite acceder a los proveedores de contenido desde la interfaz. Las aplicaciones normales nunca deberían necesitarlo."</string> - <string name="permlab_updateLock" msgid="3527558366616680889">"desalentar a las actualizaciones automáticas de dispositivos"</string> - <string name="permdesc_updateLock" msgid="1655625832166778492">"Permite a su titular a ofrecer información al sistema acerca de cuándo sería un buen momento para reiniciar el sistema no interactivo para actualizar el dispositivo."</string> + <string name="permlab_updateLock" msgid="3527558366616680889">"no realizar actualizaciones automáticas"</string> + <string name="permdesc_updateLock" msgid="1655625832166778492">"Permite a su propietario ofrecer información al sistema acerca de cuándo sería adecuado reiniciar el sistema de forma no interactiva y actualizar el dispositivo."</string> <string name="save_password_message" msgid="767344687139195790">"¿Quieres recordar esta contraseña en el navegador?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Ahora no."</string> <string name="save_password_remember" msgid="6491879678996749466">"Recuerda"</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 5bb95afec18c..385bebb47219 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -769,7 +769,7 @@ <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"akses pembekal kandungan secara luaran"</string> <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Membolehkan pemegang mengakses pembekal kandungan dari luar. Tidak akan sekali-kali diperlukan untuk apl biasa."</string> <string name="permlab_updateLock" msgid="3527558366616680889">"tidak menggalakkan kemas kini peranti automatik"</string> - <string name="permdesc_updateLock" msgid="1655625832166778492">"Membenarkan pemegang untuk menawarkan maklumat kepada sistem tentang bila akan menjadi masa yang baik untuk but semula bukan interaktif untuk menaik taraf peranti."</string> + <string name="permdesc_updateLock" msgid="1655625832166778492">"Membenarkan aplikasi untuk menawarkan maklumat kepada sistem tentang bila akan menjadi masa yang baik untuk but semula bukan interaktif untuk menaik taraf peranti."</string> <string name="save_password_message" msgid="767344687139195790">"Adakah anda mahu penyemak imbas mengingati kata laluan ini?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Bukan sekarang"</string> <string name="save_password_remember" msgid="6491879678996749466">"Ingat"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 6a9f84688386..e70a6649d57d 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -671,7 +671,7 @@ <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Du har overskredet grensen for opplåsingsforsøk med Ansiktslås"</string> <string name="lockscreen_plugged_in" msgid="8057762828355572315">"Lader, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string> <string name="lockscreen_charged" msgid="4938930459620989972">"Fullt ladet"</string> - <string name="lockscreen_battery_short" msgid="4477264849386850266">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string> + <string name="lockscreen_battery_short" msgid="4477264849386850266">"<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string> <string name="lockscreen_low_battery" msgid="1482873981919249740">"Koble til en batterilader."</string> <string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Mangler SIM-kort."</string> <string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Nettbrettet mangler SIM-kort."</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 286c32c665b1..96dcfa023873 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -768,8 +768,8 @@ <string name="permdesc_serialPort" msgid="2991639985224598193">"Umożliwia posiadaczowi dostęp do portów szeregowych przy użyciu interfejsu API narzędzia SerialManager."</string> <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"Dostęp do dostawców treści z zewnątrz"</string> <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Pozwala na dostęp do dostawców treści z powłoki. To uprawnienie nie powinno być potrzebne zwykłym aplikacjom."</string> - <string name="permlab_updateLock" msgid="3527558366616680889">"odradź automatyczne aktualizacje urządzenia"</string> - <string name="permdesc_updateLock" msgid="1655625832166778492">"Umożliwia posiadaczowi poinformowanie systemu, kiedy będzie dobry moment na nieinteraktywne uruchomienie ponowne wymagane do uaktualnienia urządzenia."</string> + <string name="permlab_updateLock" msgid="3527558366616680889">"odradzanie automatycznych aktualizacji urządzenia"</string> + <string name="permdesc_updateLock" msgid="1655625832166778492">"Umożliwia posiadaczowi poinformowanie systemu, kiedy będzie dobry moment na ponowne uruchomienie wymagane do uaktualnienia urządzenia."</string> <string name="save_password_message" msgid="767344687139195790">"Czy chcesz, aby zapamiętać to hasło w przeglądarce?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Nie teraz"</string> <string name="save_password_remember" msgid="6491879678996749466">"Zapamiętaj"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 286e6d66681a..13053ab22667 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -768,8 +768,8 @@ <string name="permdesc_serialPort" msgid="2991639985224598193">"Inaruhusu mmiliki kufikia vituo tambulishi kwa kutumia KisimamiziTambulishi cha API."</string> <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"fikia watoa huduma nje"</string> <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Inaruhusu mmiliki kufikia watoa huduma kutoka kwa onyesho. Haifai kuhitajika kamwe kwa programu za kawaida."</string> - <string name="permlab_updateLock" msgid="3527558366616680889">"katisha tamaa usasishaji kifaa kiotomatiki"</string> - <string name="permdesc_updateLock" msgid="1655625832166778492">"Inaruhusu mmiliki kutoa maelezo kwa mfumo kuhusu ni lini itakuwa wakati mzuri wa uwashaji upya usiotagusana ili kuboresha kifaa."</string> + <string name="permlab_updateLock" msgid="3527558366616680889">"pinga usasishaji kifaa kiotomatiki"</string> + <string name="permdesc_updateLock" msgid="1655625832166778492">"Inaruhusu mmiliki kutoa maelezo kwa mfumo kuhusu ni lini itakuwa wakati mzuri wa uwashaji upya usiotagusana ili kupandisha gredi kifaa."</string> <string name="save_password_message" msgid="767344687139195790">"Unataka kuvinjari ili ukumbuke nenosiri hili?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Si Sasa"</string> <string name="save_password_remember" msgid="6491879678996749466">"Kumbuka"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 24b726d3c102..2664ad339558 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -769,7 +769,7 @@ <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"从外部访问内容提供程序"</string> <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"允许持有者通过界面访问内容提供程序。普通应用绝不需要此权限。"</string> <string name="permlab_updateLock" msgid="3527558366616680889">"阻止自动设备更新"</string> - <string name="permdesc_updateLock" msgid="1655625832166778492">"允许持有人向系统提供相关信息,以确定什么时候适合执行非交互式重新启动来升级设备。"</string> + <string name="permdesc_updateLock" msgid="1655625832166778492">"允许应用向系统提供相关信息,以确定何时适合执行非交互式重启以升级设备。"</string> <string name="save_password_message" msgid="767344687139195790">"是否希望浏览器记住此密码?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"暂不保存"</string> <string name="save_password_remember" msgid="6491879678996749466">"记住"</string> diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java index 259f15fcdc2a..19aa77baf94f 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java @@ -241,6 +241,7 @@ public class ConnectivityManagerTestActivity extends Activity { mCM = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); // Get an instance of WifiManager mWifiManager =(WifiManager)getSystemService(Context.WIFI_SERVICE); + mContext = this; mChannel = mWifiManager.initialize(mContext, mContext.getMainLooper(), null); initializeNetworkStates(); diff --git a/core/tests/coretests/src/android/content/SyncOperationTest.java b/core/tests/coretests/src/android/content/SyncOperationTest.java index 37e948d8b0a6..910c7217fec6 100644 --- a/core/tests/coretests/src/android/content/SyncOperationTest.java +++ b/core/tests/coretests/src/android/content/SyncOperationTest.java @@ -41,7 +41,7 @@ public class SyncOperationTest extends AndroidTestCase { Bundle b2 = new Bundle(); b2.putBoolean("b2", true); - SyncOperation op1 = new SyncOperation(account1, + SyncOperation op1 = new SyncOperation(account1, 0, 1, "authority1", b1, @@ -51,7 +51,7 @@ public class SyncOperationTest extends AndroidTestCase { false); // Same as op1 but different time infos - SyncOperation op2 = new SyncOperation(account1, + SyncOperation op2 = new SyncOperation(account1, 0, 1, "authority1", b1, @@ -61,7 +61,7 @@ public class SyncOperationTest extends AndroidTestCase { false); // Same as op1 but different authority - SyncOperation op3 = new SyncOperation(account1, + SyncOperation op3 = new SyncOperation(account1, 0, 1, "authority2", b1, @@ -71,7 +71,7 @@ public class SyncOperationTest extends AndroidTestCase { false); // Same as op1 but different account - SyncOperation op4 = new SyncOperation(account2, + SyncOperation op4 = new SyncOperation(account2, 0, 1, "authority1", b1, @@ -81,7 +81,7 @@ public class SyncOperationTest extends AndroidTestCase { false); // Same as op1 but different bundle - SyncOperation op5 = new SyncOperation(account1, + SyncOperation op5 = new SyncOperation(account1, 0, 1, "authority1", b2, diff --git a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java index ae4140926cee..96f313a1e29f 100644 --- a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java +++ b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java @@ -20,6 +20,7 @@ import com.android.internal.os.AtomicFile; import android.accounts.Account; import android.os.Bundle; +import android.os.Debug; import android.test.AndroidTestCase; import android.test.RenamingDelegatingContext; import android.test.mock.MockContentResolver; @@ -34,6 +35,10 @@ import java.util.List; public class SyncStorageEngineTest extends AndroidTestCase { + private File getSyncDir() { + return new File(new File(getContext().getFilesDir(), "system"), "sync"); + } + /** * Test that we handle the case of a history row being old enough to purge before the * correcponding sync is finished. This can happen if the clock changes while we are syncing. @@ -52,7 +57,7 @@ public class SyncStorageEngineTest extends AndroidTestCase { long time0 = 1000; long historyId = engine.insertStartSyncEvent( - account, authority, time0, SyncStorageEngine.SOURCE_LOCAL); + account, 0, authority, time0, SyncStorageEngine.SOURCE_LOCAL); long time1 = time0 + SyncStorageEngine.MILLIS_IN_4WEEKS * 2; engine.stopSyncEvent(historyId, time1 - time0, "yay", 0, 0); } @@ -82,38 +87,47 @@ public class SyncStorageEngineTest extends AndroidTestCase { SyncStorageEngine engine = SyncStorageEngine.newTestInstance( new TestContext(mockResolver, getContext())); - removePeriodicSyncs(engine, account1, authority); - removePeriodicSyncs(engine, account2, authority); + removePeriodicSyncs(engine, account1, 0, authority); + removePeriodicSyncs(engine, account2, 0, authority); + removePeriodicSyncs(engine, account1, 1, authority); // this should add two distinct periodic syncs for account1 and one for account2 - engine.addPeriodicSync(sync1.account, sync1.authority, sync1.extras, sync1.period); - engine.addPeriodicSync(sync2.account, sync2.authority, sync2.extras, sync2.period); - engine.addPeriodicSync(sync3.account, sync3.authority, sync3.extras, sync3.period); - engine.addPeriodicSync(sync4.account, sync4.authority, sync4.extras, sync4.period); + engine.addPeriodicSync(sync1.account, 0, sync1.authority, sync1.extras, sync1.period); + engine.addPeriodicSync(sync2.account, 0, sync2.authority, sync2.extras, sync2.period); + engine.addPeriodicSync(sync3.account, 0, sync3.authority, sync3.extras, sync3.period); + engine.addPeriodicSync(sync4.account, 0, sync4.authority, sync4.extras, sync4.period); + // add a second user + engine.addPeriodicSync(sync2.account, 1, sync2.authority, sync2.extras, sync2.period); - List<PeriodicSync> syncs = engine.getPeriodicSyncs(account1, authority); + List<PeriodicSync> syncs = engine.getPeriodicSyncs(account1, 0, authority); assertEquals(2, syncs.size()); assertEquals(sync1, syncs.get(0)); assertEquals(sync3, syncs.get(1)); - engine.removePeriodicSync(sync1.account, sync1.authority, sync1.extras); + engine.removePeriodicSync(sync1.account, 0, sync1.authority, sync1.extras); - syncs = engine.getPeriodicSyncs(account1, authority); + syncs = engine.getPeriodicSyncs(account1, 0, authority); assertEquals(1, syncs.size()); assertEquals(sync3, syncs.get(0)); - syncs = engine.getPeriodicSyncs(account2, authority); + syncs = engine.getPeriodicSyncs(account2, 0, authority); assertEquals(1, syncs.size()); assertEquals(sync4, syncs.get(0)); + + syncs = engine.getPeriodicSyncs(sync2.account, 1, sync2.authority); + assertEquals(1, syncs.size()); + assertEquals(sync2, syncs.get(0)); } - private void removePeriodicSyncs(SyncStorageEngine engine, Account account, String authority) { - engine.setIsSyncable(account, authority, engine.getIsSyncable(account, authority)); - List<PeriodicSync> syncs = engine.getPeriodicSyncs(account, authority); + private void removePeriodicSyncs(SyncStorageEngine engine, Account account, int userId, + String authority) { + engine.setIsSyncable(account, userId, authority, + engine.getIsSyncable(account, 0, authority)); + List<PeriodicSync> syncs = engine.getPeriodicSyncs(account, userId, authority); for (PeriodicSync sync : syncs) { - engine.removePeriodicSync(sync.account, sync.authority, sync.extras); + engine.removePeriodicSync(sync.account, userId, sync.authority, sync.extras); } } @@ -147,57 +161,57 @@ public class SyncStorageEngineTest extends AndroidTestCase { SyncStorageEngine engine = SyncStorageEngine.newTestInstance( new TestContext(mockResolver, getContext())); - removePeriodicSyncs(engine, account1, authority1); - removePeriodicSyncs(engine, account2, authority1); - removePeriodicSyncs(engine, account1, authority2); - removePeriodicSyncs(engine, account2, authority2); + removePeriodicSyncs(engine, account1, 0, authority1); + removePeriodicSyncs(engine, account2, 0, authority1); + removePeriodicSyncs(engine, account1, 0, authority2); + removePeriodicSyncs(engine, account2, 0, authority2); - engine.setMasterSyncAutomatically(false); + engine.setMasterSyncAutomatically(false, 0); - engine.setIsSyncable(account1, authority1, 1); - engine.setSyncAutomatically(account1, authority1, true); + engine.setIsSyncable(account1, 0, authority1, 1); + engine.setSyncAutomatically(account1, 0, authority1, true); - engine.setIsSyncable(account2, authority1, 1); - engine.setSyncAutomatically(account2, authority1, true); + engine.setIsSyncable(account2, 0, authority1, 1); + engine.setSyncAutomatically(account2, 0, authority1, true); - engine.setIsSyncable(account1, authority2, 1); - engine.setSyncAutomatically(account1, authority2, false); + engine.setIsSyncable(account1, 0, authority2, 1); + engine.setSyncAutomatically(account1, 0, authority2, false); - engine.setIsSyncable(account2, authority2, 0); - engine.setSyncAutomatically(account2, authority2, true); + engine.setIsSyncable(account2, 0, authority2, 0); + engine.setSyncAutomatically(account2, 0, authority2, true); - engine.addPeriodicSync(sync1.account, sync1.authority, sync1.extras, sync1.period); - engine.addPeriodicSync(sync2.account, sync2.authority, sync2.extras, sync2.period); - engine.addPeriodicSync(sync3.account, sync3.authority, sync3.extras, sync3.period); - engine.addPeriodicSync(sync4.account, sync4.authority, sync4.extras, sync4.period); - engine.addPeriodicSync(sync5.account, sync5.authority, sync5.extras, sync5.period); + engine.addPeriodicSync(sync1.account, 0, sync1.authority, sync1.extras, sync1.period); + engine.addPeriodicSync(sync2.account, 0, sync2.authority, sync2.extras, sync2.period); + engine.addPeriodicSync(sync3.account, 0, sync3.authority, sync3.extras, sync3.period); + engine.addPeriodicSync(sync4.account, 0, sync4.authority, sync4.extras, sync4.period); + engine.addPeriodicSync(sync5.account, 0, sync5.authority, sync5.extras, sync5.period); engine.writeAllState(); engine.clearAndReadState(); - List<PeriodicSync> syncs = engine.getPeriodicSyncs(account1, authority1); + List<PeriodicSync> syncs = engine.getPeriodicSyncs(account1, 0, authority1); assertEquals(2, syncs.size()); assertEquals(sync1, syncs.get(0)); assertEquals(sync2, syncs.get(1)); - syncs = engine.getPeriodicSyncs(account1, authority2); + syncs = engine.getPeriodicSyncs(account1, 0, authority2); assertEquals(2, syncs.size()); assertEquals(sync3, syncs.get(0)); assertEquals(sync4, syncs.get(1)); - syncs = engine.getPeriodicSyncs(account2, authority1); + syncs = engine.getPeriodicSyncs(account2, 0, authority1); assertEquals(1, syncs.size()); assertEquals(sync5, syncs.get(0)); - assertEquals(true, engine.getSyncAutomatically(account1, authority1)); - assertEquals(true, engine.getSyncAutomatically(account2, authority1)); - assertEquals(false, engine.getSyncAutomatically(account1, authority2)); - assertEquals(true, engine.getSyncAutomatically(account2, authority2)); + assertEquals(true, engine.getSyncAutomatically(account1, 0, authority1)); + assertEquals(true, engine.getSyncAutomatically(account2, 0, authority1)); + assertEquals(false, engine.getSyncAutomatically(account1, 0, authority2)); + assertEquals(true, engine.getSyncAutomatically(account2, 0, authority2)); - assertEquals(1, engine.getIsSyncable(account1, authority1)); - assertEquals(1, engine.getIsSyncable(account2, authority1)); - assertEquals(1, engine.getIsSyncable(account1, authority2)); - assertEquals(0, engine.getIsSyncable(account2, authority2)); + assertEquals(1, engine.getIsSyncable(account1, 0, authority1)); + assertEquals(1, engine.getIsSyncable(account2, 0, authority1)); + assertEquals(1, engine.getIsSyncable(account1, 0, authority2)); + assertEquals(0, engine.getIsSyncable(account2, 0, authority2)); } @MediumTest @@ -220,12 +234,13 @@ public class SyncStorageEngineTest extends AndroidTestCase { byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + "<accounts>\n" - + "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n" - + "<authority id=\"1\" account=\"account1\" type=\"type1\" authority=\"auth2\" />\n" - + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n" + + "<authority id=\"0\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n" + + "<authority id=\"1\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth2\" />\n" + + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n" + + "<authority id=\"3\" user=\"1\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n" + "</accounts>\n").getBytes(); - File syncDir = new File(new File(testContext.getFilesDir(), "system"), "sync"); + File syncDir = getSyncDir(); syncDir.mkdirs(); AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); FileOutputStream fos = accountInfoFile.startWrite(); @@ -234,15 +249,19 @@ public class SyncStorageEngineTest extends AndroidTestCase { SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); - List<PeriodicSync> syncs = engine.getPeriodicSyncs(account, authority1); + List<PeriodicSync> syncs = engine.getPeriodicSyncs(account, 0, authority1); assertEquals(1, syncs.size()); assertEquals(sync1, syncs.get(0)); - syncs = engine.getPeriodicSyncs(account, authority2); + syncs = engine.getPeriodicSyncs(account, 0, authority2); assertEquals(1, syncs.size()); assertEquals(sync2, syncs.get(0)); - syncs = engine.getPeriodicSyncs(account, authority3); + syncs = engine.getPeriodicSyncs(account, 0, authority3); + assertEquals(1, syncs.size()); + assertEquals(sync3, syncs.get(0)); + + syncs = engine.getPeriodicSyncs(account, 1, authority3); assertEquals(1, syncs.size()); assertEquals(sync3, syncs.get(0)); @@ -260,13 +279,13 @@ public class SyncStorageEngineTest extends AndroidTestCase { engine.clearAndReadState(); - syncs = engine.getPeriodicSyncs(account, authority1); + syncs = engine.getPeriodicSyncs(account, 0, authority1); assertEquals(0, syncs.size()); - syncs = engine.getPeriodicSyncs(account, authority2); + syncs = engine.getPeriodicSyncs(account, 0, authority2); assertEquals(0, syncs.size()); - syncs = engine.getPeriodicSyncs(account, authority3); + syncs = engine.getPeriodicSyncs(account, 0, authority3); assertEquals(0, syncs.size()); accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" @@ -289,20 +308,48 @@ public class SyncStorageEngineTest extends AndroidTestCase { engine.clearAndReadState(); - syncs = engine.getPeriodicSyncs(account, authority1); + syncs = engine.getPeriodicSyncs(account, 0, authority1); assertEquals(1, syncs.size()); assertEquals(sync1s, syncs.get(0)); - syncs = engine.getPeriodicSyncs(account, authority2); + syncs = engine.getPeriodicSyncs(account, 0, authority2); assertEquals(1, syncs.size()); assertEquals(sync2s, syncs.get(0)); - syncs = engine.getPeriodicSyncs(account, authority3); + syncs = engine.getPeriodicSyncs(account, 0, authority3); assertEquals(1, syncs.size()); assertEquals(sync3s, syncs.get(0)); } @MediumTest + public void testListenForTicklesParsing() throws Exception { + byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<accounts>\n" + + "<listenForTickles user=\"0\" enabled=\"false\" />" + + "<listenForTickles user=\"1\" enabled=\"true\" />" + + "<authority id=\"0\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n" + + "<authority id=\"1\" user=\"1\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n" + + "</accounts>\n").getBytes(); + + MockContentResolver mockResolver = new MockContentResolver(); + final TestContext testContext = new TestContext(mockResolver, getContext()); + + File syncDir = getSyncDir(); + syncDir.mkdirs(); + AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); + FileOutputStream fos = accountInfoFile.startWrite(); + fos.write(accountsFileData); + accountInfoFile.finishWrite(fos); + + SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); + + assertEquals(false, engine.getMasterSyncAutomatically(0)); + assertEquals(true, engine.getMasterSyncAutomatically(1)); + assertEquals(true, engine.getMasterSyncAutomatically(2)); + + } + + @MediumTest public void testAuthorityRenaming() throws Exception { final Account account1 = new Account("acc1", "type1"); final Account account2 = new Account("acc2", "type2"); @@ -339,17 +386,17 @@ public class SyncStorageEngineTest extends AndroidTestCase { SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); - assertEquals(false, engine.getSyncAutomatically(account1, authorityContacts)); - assertEquals(false, engine.getSyncAutomatically(account1, authorityCalendar)); - assertEquals(true, engine.getSyncAutomatically(account1, authorityOther)); - assertEquals(true, engine.getSyncAutomatically(account1, authorityContactsNew)); - assertEquals(true, engine.getSyncAutomatically(account1, authorityCalendarNew)); - - assertEquals(false, engine.getSyncAutomatically(account2, authorityContacts)); - assertEquals(false, engine.getSyncAutomatically(account2, authorityCalendar)); - assertEquals(true, engine.getSyncAutomatically(account2, authorityOther)); - assertEquals(false, engine.getSyncAutomatically(account2, authorityContactsNew)); - assertEquals(false, engine.getSyncAutomatically(account2, authorityCalendarNew)); + assertEquals(false, engine.getSyncAutomatically(account1, 0, authorityContacts)); + assertEquals(false, engine.getSyncAutomatically(account1, 0, authorityCalendar)); + assertEquals(true, engine.getSyncAutomatically(account1, 0, authorityOther)); + assertEquals(true, engine.getSyncAutomatically(account1, 0, authorityContactsNew)); + assertEquals(true, engine.getSyncAutomatically(account1, 0, authorityCalendarNew)); + + assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityContacts)); + assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityCalendar)); + assertEquals(true, engine.getSyncAutomatically(account2, 0, authorityOther)); + assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityContactsNew)); + assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityCalendarNew)); } @SmallTest @@ -379,10 +426,10 @@ public class SyncStorageEngineTest extends AndroidTestCase { SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); - assertEquals(-1, engine.getIsSyncable(account, "other1")); - assertEquals(1, engine.getIsSyncable(account, "other2")); - assertEquals(0, engine.getIsSyncable(account, "other3")); - assertEquals(1, engine.getIsSyncable(account, "other4")); + assertEquals(-1, engine.getIsSyncable(account, 0, "other1")); + assertEquals(1, engine.getIsSyncable(account, 0, "other2")); + assertEquals(0, engine.getIsSyncable(account, 0, "other3")); + assertEquals(1, engine.getIsSyncable(account, 0, "other4")); } } diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h index d54ab357e86a..cc0a594d164d 100644 --- a/include/media/AudioSystem.h +++ b/include/media/AudioSystem.h @@ -185,7 +185,7 @@ public: audio_devices_t device); static uint32_t getStrategyForStream(audio_stream_type_t stream); - static uint32_t getDevicesForStream(audio_stream_type_t stream); + static audio_devices_t getDevicesForStream(audio_stream_type_t stream); static audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc); static status_t registerEffect(effect_descriptor_t *desc, diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h index bdd774748a02..04c927aec3bd 100644 --- a/include/media/IAudioPolicyService.h +++ b/include/media/IAudioPolicyService.h @@ -79,7 +79,7 @@ public: int *index, audio_devices_t device) = 0; virtual uint32_t getStrategyForStream(audio_stream_type_t stream) = 0; - virtual uint32_t getDevicesForStream(audio_stream_type_t stream) = 0; + virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream) = 0; virtual audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc) = 0; virtual status_t registerEffect(effect_descriptor_t *desc, audio_io_handle_t io, diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 6ec5d2073236..f572f71458fc 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -36,6 +36,7 @@ #include "utils/String8.h" #include "android_media_Utils.h" +#include "android_os_Parcel.h" #include "android_util_Binder.h" #include <binder/Parcel.h> #include <gui/ISurfaceTexture.h> diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index e0b186adfde5..a1cbf0f2bd85 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -701,10 +701,10 @@ uint32_t AudioSystem::getStrategyForStream(audio_stream_type_t stream) return aps->getStrategyForStream(stream); } -uint32_t AudioSystem::getDevicesForStream(audio_stream_type_t stream) +audio_devices_t AudioSystem::getDevicesForStream(audio_stream_type_t stream) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); - if (aps == 0) return 0; + if (aps == 0) return (audio_devices_t)0; return aps->getDevicesForStream(stream); } diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp index 99385aa4f80a..da7c1244e16e 100644 --- a/media/libmedia/IAudioPolicyService.cpp +++ b/media/libmedia/IAudioPolicyService.cpp @@ -267,13 +267,13 @@ public: return reply.readInt32(); } - virtual uint32_t getDevicesForStream(audio_stream_type_t stream) + virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream) { Parcel data, reply; data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor()); data.writeInt32(static_cast <uint32_t>(stream)); remote()->transact(GET_DEVICES_FOR_STREAM, data, &reply); - return (uint32_t) reply.readInt32(); + return (audio_devices_t) reply.readInt32(); } virtual audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc) diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index b21e86a68129..9e00bb349144 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -2108,6 +2108,8 @@ status_t AwesomePlayer::finishSetDataSource_l() { mWVMExtractor = new WVMExtractor(dataSource); mWVMExtractor->setAdaptiveStreamingMode(true); + if (mUIDValid) + mWVMExtractor->setUID(mUID); extractor = mWVMExtractor; } else { extractor = MediaExtractor::Create( diff --git a/media/libstagefright/WVMExtractor.cpp b/media/libstagefright/WVMExtractor.cpp index c7ad513baf10..dac8106a6858 100644 --- a/media/libstagefright/WVMExtractor.cpp +++ b/media/libstagefright/WVMExtractor.cpp @@ -123,6 +123,12 @@ void WVMExtractor::setAdaptiveStreamingMode(bool adaptive) { } } +void WVMExtractor::setUID(uid_t uid) { + if (mImpl != NULL) { + mImpl->setUID(uid); + } +} + bool SniffWVM( const sp<DataSource> &source, String8 *mimeType, float *confidence, sp<AMessage> *) { diff --git a/media/libstagefright/include/WVMExtractor.h b/media/libstagefright/include/WVMExtractor.h index 9f763f9debef..3c3ca892259a 100644 --- a/media/libstagefright/include/WVMExtractor.h +++ b/media/libstagefright/include/WVMExtractor.h @@ -34,6 +34,7 @@ public: virtual int64_t getCachedDurationUs(status_t *finalStatus) = 0; virtual void setAdaptiveStreamingMode(bool adaptive) = 0; + virtual void setUID(uid_t uid) = 0; }; class WVMExtractor : public MediaExtractor { @@ -60,6 +61,8 @@ public: // is used. void setAdaptiveStreamingMode(bool adaptive); + void setUID(uid_t uid); + static bool getVendorLibHandle(); protected: diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 7c1cd18b8a32..1e826469860e 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -26,7 +26,7 @@ <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Hapus dari daftar"</string> <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Info apl"</string> <string name="status_bar_no_recent_apps" msgid="6576392951053994640">"Tidak ada apl terbaru"</string> - <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Singkirkan aplikasi terbaru"</string> + <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Tutup aplikasi terbaru"</string> <plurals name="status_bar_accessibility_recent_apps"> <item quantity="one" msgid="5854176083865845541">"1 apl terbaru"</item> <item quantity="other" msgid="1040784359794890744">"%d apl terbaru"</item> diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp index 753b1d248323..62768bf4fcd8 100644 --- a/services/audioflinger/AudioPolicyService.cpp +++ b/services/audioflinger/AudioPolicyService.cpp @@ -432,10 +432,12 @@ uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream) return mpAudioPolicy->get_strategy_for_stream(mpAudioPolicy, stream); } -uint32_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stream) +//audio policy: use audio_device_t appropriately + +audio_devices_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stream) { if (mpAudioPolicy == NULL) { - return 0; + return (audio_devices_t)0; } return mpAudioPolicy->get_devices_for_stream(mpAudioPolicy, stream); } diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h index 962c91750bd9..e41d51e1d582 100644 --- a/services/audioflinger/AudioPolicyService.h +++ b/services/audioflinger/AudioPolicyService.h @@ -95,7 +95,7 @@ public: audio_devices_t device); virtual uint32_t getStrategyForStream(audio_stream_type_t stream); - virtual uint32_t getDevicesForStream(audio_stream_type_t stream); + virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream); virtual audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc); virtual status_t registerEffect(effect_descriptor_t *desc, diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index c5e7b6237680..fd968e0b7330 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -4297,7 +4297,7 @@ public final class ActivityManagerService extends ActivityManagerNative try { int uid = AppGlobals.getPackageManager() .getPackageUid(rec.key.packageName); - if (uid != Binder.getCallingUid()) { + if (!UserId.isSameApp(uid, Binder.getCallingUid())) { String msg = "Permission Denial: cancelIntentSender() from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index 19fe1bfba801..a4e573d805b8 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -1821,6 +1821,10 @@ final class ActivityStack { if (below != null && below.finishing) { continue; } + // Don't check any lower in the stack if we're crossing a user boundary. + if (below != null && below.userId != taskTop.userId) { + break; + } if (target == null) { target = below; targetI = i; diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 023f97dbb1d4..18b51a7fd411 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -6579,7 +6579,7 @@ public class WindowManagerService extends IWindowManager.Stub for (int i = count - 1; i >= 0; i--) { WindowState win = mWindows.get(i); try { - win.mClient.dispatchScreenStatus(on); + win.mClient.dispatchScreenState(on); } catch (RemoteException e) { // Ignored } @@ -9173,7 +9173,7 @@ public class WindowManagerService extends IWindowManager.Stub void scheduleAnimationLocked() { if (!mAnimationScheduled) { - mChoreographer.postAnimationCallback(mAnimationRunnable); + mChoreographer.postAnimationCallback(mAnimationRunnable, null); mAnimationScheduled = true; } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java index 965f553bbbc0..7c683c9e1f5a 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java @@ -53,7 +53,8 @@ public final class BridgeWindow implements IWindow { } @Override - public void dispatchScreenStatus(boolean on) throws RemoteException { + public void dispatchScreenState(boolean on) throws RemoteException { + // pass for now. } @Override |