Merge "Fix crash."
diff --git a/api/current.txt b/api/current.txt
index f79df94..77c9f6e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10786,8 +10786,8 @@
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 @@
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 @@
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 @@
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 5fee4de..adc7d35 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -18,6 +18,7 @@
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.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.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.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 @@
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 @@
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 @@
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 @@
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 @@
mAuthenticatorCache = authenticatorCache;
mAuthenticatorCache.setListener(this, null /* Handler */);
+ UserAccounts accounts = initUser(0);
+
sThis.set(this);
IntentFilter intentFilter = new IntentFilter();
@@ -211,17 +227,36 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
db.setTransactionSuccessful();
- insertAccountIntoCacheLocked(account);
+ insertAccountIntoCacheLocked(accounts, account);
} finally {
db.endTransaction();
}
- sendAccountsChangedBroadcast();
+ sendAccountsChangedBroadcast(accounts.userId);
return true;
}
}
@@ -467,9 +538,10 @@
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 @@
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 @@
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 @@
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 @@
&& !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 @@
}
}
+ /* 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 @@
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 @@
}
}
- 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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
}
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 @@
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 @@
}
}
- writeUserDataIntoCacheLocked(db, account, key, value);
+ writeUserDataIntoCacheLocked(accounts, db, account, key, value);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
@@ -868,15 +956,16 @@
}
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 @@
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 @@
// 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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+ /**
+ * 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 @@
+ ", 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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
}
}
- 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 @@
+ " 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 @@
}
}
} 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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
}
}
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 @@
}
} 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 @@
}
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 @@
}
}
- 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 @@
}
}
- 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 @@
}
}
- 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 0ee683c..4419c8c 100644
--- a/core/java/android/accounts/GrantCredentialsPermissionActivity.java
+++ b/core/java/android/accounts/GrantCredentialsPermissionActivity.java
@@ -113,8 +113,7 @@
}
};
- 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 38b371d..2b4ee50 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 6fbeee3..954ae66 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -645,7 +645,7 @@
// 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 fc4c262..f827c3d 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -26,6 +26,7 @@
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 @@
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 @@
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 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 @@
* @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 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 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 @@
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 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 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 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 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 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 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 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 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 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 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 ba24036..b7dfe92 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -23,18 +23,23 @@
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.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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
* <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 @@
* @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 @@
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 @@
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 @@
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 @@
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 @@
+ ", 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 @@
+ ", 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 @@
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 @@
}
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 @@
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 @@
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 @@
} 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 @@
* @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 @@
* @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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
: 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 @@
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 @@
// 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 @@
// 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 @@
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 @@
// 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 @@
}
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 @@
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 @@
}
}
- 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 @@
// 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 @@
}
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 @@
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 @@
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 94c2247..4e86ef8 100644
--- a/core/java/android/content/SyncOperation.java
+++ b/core/java/android/content/SyncOperation.java
@@ -26,6 +26,7 @@
*/
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 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 @@
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 @@
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 bfdf4a1..06da6fa 100644
--- a/core/java/android/content/SyncQueue.java
+++ b/core/java/android/content/SyncQueue.java
@@ -49,7 +49,8 @@
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 @@
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 @@
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 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 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 @@
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 a1e174b..7bb9866 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -25,6 +25,7 @@
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 @@
* @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 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 @@
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 @@
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 @@
}
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 @@
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 @@
}
}
+ 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 @@
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 @@
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 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 @@
i--;
AuthorityInfo authority = mAuthorities.valueAt(i);
if (authority.authority.equals(providerName)
+ && authority.userId == userId
&& authority.enabled) {
return true;
}
@@ -410,11 +444,13 @@
}
}
- 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 @@
}
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 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 @@
}
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 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 @@
}
} 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 @@
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 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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* 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 @@
}
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 @@
/**
* 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 @@
/**
* 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 @@
* 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 @@
* @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 @@
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 @@
/**
* 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 @@
if (ainfo == null) {
continue;
}
+ if (userId != ainfo.userId) {
+ continue;
+ }
if (account != null && !ainfo.account.equals(account)) {
continue;
}
@@ -1235,34 +1290,6 @@
}
}
- /**
- * 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 @@
* @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 @@
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 @@
}
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 @@
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 @@
}
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 @@
} 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 @@
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 @@
}
// 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 @@
}
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 215e836..6c1445d 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -28,6 +28,8 @@
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 @@
/**
* 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 @@
* 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 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 f4d7af9..10edc06 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -81,8 +81,8 @@
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 @@
}
/**
+ * 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 @@
// 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 @@
}
}
- 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 @@
void doAnimationInner() {
final long start;
- final Callback callbacks;
+ Callback callbacks;
synchronized (mLock) {
if (!mAnimationScheduled) {
return; // no work to do
@@ -351,7 +374,23 @@
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 @@
void doDraw() {
final long start;
- final Callback callbacks;
+ Callback callbacks;
synchronized (mLock) {
if (!mDrawScheduled) {
return; // no work to do
@@ -383,7 +422,23 @@
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 @@
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;
}
- tail.next = callback;
+ while (entry.next != null) {
+ if (dueTime < entry.next.dueTime) {
+ callback.next = entry.next;
+ break;
+ }
+ entry = entry.next;
+ }
+ 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 @@
private void runCallbacks(Callback head) {
while (head != null) {
- head.runnable.run();
+ head.action.run();
head = head.next;
}
}
@@ -461,7 +544,7 @@
}
}
- 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 @@
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 @@
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 @@
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 acb1387..497bc90b 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -49,7 +49,7 @@
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 94531c8..a651362 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1994,6 +1994,20 @@
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 @@
}
/**
+ * <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 @@
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 @@
}
/**
+ * @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 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 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 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 0c63286..c9e0242 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2254,6 +2254,17 @@
}
@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 7fd05c3..72365c7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -761,9 +761,12 @@
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 @@
void scheduleFrame() {
if (!mFrameScheduled) {
mFrameScheduled = true;
- mChoreographer.postDrawCallback(mFrameRunnable);
+ mChoreographer.postDrawCallback(mFrameRunnable, null);
}
}
@@ -890,7 +893,7 @@
if (mFrameScheduled) {
mFrameScheduled = false;
- mChoreographer.removeDrawCallbacks(mFrameRunnable);
+ mChoreographer.removeDrawCallbacks(mFrameRunnable, null);
}
}
@@ -2500,7 +2503,7 @@
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 @@
.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 @@
}
if (mPosted && mViews.isEmpty() && mViewRects.isEmpty()) {
- mChoreographer.removeAnimationCallbacks(this);
+ mChoreographer.removeAnimationCallbacks(this, null);
mPosted = false;
}
}
@@ -4089,7 +4092,7 @@
private void postIfNeededLocked() {
if (!mPosted) {
- mChoreographer.postAnimationCallback(this);
+ mChoreographer.postAnimationCallback(this, null);
mPosted = true;
}
}
@@ -4142,8 +4145,8 @@
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 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 334b9c4..23e55e2 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -169,6 +169,8 @@
/**
* 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 @@
}
/**
+ * 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 5ee7392..4c89218 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4280,6 +4280,12 @@
}
@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 @@
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 6a99a2b..998c037 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -348,6 +348,7 @@
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 @@
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 e695f8e..15d11d8 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -49,7 +49,7 @@
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 c389cf7..642988b 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -64,6 +64,7 @@
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 3067e75..de9fd33 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -130,6 +130,7 @@
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 @@
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 d1d3b78..5e73a5f 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 5c6ebdf..866d223 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 579d6ad..ea02f53 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 0000000..8a99049
--- /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 0000000..65f3819e
--- /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 e00970a..0f99fb2 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 parcel_offsets_t
-{
- jfieldID mObject;
- jfieldID mOwnObject;
-} gParcelOffsets;
-
static struct log_offsets_t
{
// Class state.
@@ -232,15 +228,6 @@
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 @@
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 jint android_os_Parcel_dataSize(JNIEnv* env, jobject clazz)
+int register_android_os_Binder(JNIEnv* env)
{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- return parcel ? parcel->dataSize() : 0;
-}
+ 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 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";
-
-static int int_register_android_os_Parcel(JNIEnv* env)
-{
jclass clazz;
clazz = env->FindClass("android/util/Log");
@@ -1894,14 +1281,6 @@
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 @@
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 0122691..ca320ef 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 @@
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 8350e73..9c44a59 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 0fb1b17..e69fb74 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 1a0d479..8e36948 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 5bb95af..385bebb 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 6a9f846..e70a664 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 286c32c..96dcfa0 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 286e6d6..13053ab 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 24b726d..2664ad3 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 259f15f..19aa77b 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 @@
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 37e948d..910c721 100644
--- a/core/tests/coretests/src/android/content/SyncOperationTest.java
+++ b/core/tests/coretests/src/android/content/SyncOperationTest.java
@@ -41,7 +41,7 @@
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 @@
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 @@
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 @@
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 @@
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 ae41409..96f313a 100644
--- a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
+++ b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java
@@ -20,6 +20,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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(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, 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(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 @@
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 d54ab35..cc0a594 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -185,7 +185,7 @@
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 bdd7747..04c927a 100644
--- a/include/media/IAudioPolicyService.h
+++ b/include/media/IAudioPolicyService.h
@@ -79,7 +79,7 @@
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 6ec5d20..f572f71 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 e0b186a..a1cbf0f 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -701,10 +701,10 @@
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 99385aa4..da7c124 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -267,13 +267,13 @@
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 b21e86a..9e00bb3 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -2108,6 +2108,8 @@
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 c7ad513..dac8106 100644
--- a/media/libstagefright/WVMExtractor.cpp
+++ b/media/libstagefright/WVMExtractor.cpp
@@ -123,6 +123,12 @@
}
}
+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 9f763f9..3c3ca89 100644
--- a/media/libstagefright/include/WVMExtractor.h
+++ b/media/libstagefright/include/WVMExtractor.h
@@ -34,6 +34,7 @@
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 @@
// 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 7c1cd18..1e82646 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 753b1d2..62768bf 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -432,10 +432,12 @@
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 962c917..e41d51e 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -95,7 +95,7 @@
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 c5e7b62..fd968e0 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -4297,7 +4297,7 @@
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 19fe1bf..a4e573d 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1821,6 +1821,10 @@
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 023f97d..18b51a7 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -6579,7 +6579,7 @@
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 @@
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 965f553..7c683c9 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 @@
}
@Override
- public void dispatchScreenStatus(boolean on) throws RemoteException {
+ public void dispatchScreenState(boolean on) throws RemoteException {
+ // pass for now.
}
@Override