diff options
Diffstat (limited to 'core/java/android')
28 files changed, 661 insertions, 430 deletions
diff --git a/core/java/android/accounts/AccountAuthenticatorCache.java b/core/java/android/accounts/AccountAuthenticatorCache.java index 7214c50..f937cde 100644 --- a/core/java/android/accounts/AccountAuthenticatorCache.java +++ b/core/java/android/accounts/AccountAuthenticatorCache.java @@ -16,17 +16,18 @@ package android.accounts; +import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.RegisteredServicesCache; import android.content.pm.XmlSerializerAndParser; import android.content.res.Resources; import android.content.res.TypedArray; -import android.content.Context; -import android.util.AttributeSet; import android.text.TextUtils; +import android.util.AttributeSet; + import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; import java.io.IOException; diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java index fc569e0..5cde65c 100644 --- a/core/java/android/accounts/AccountManagerService.java +++ b/core/java/android/accounts/AccountManagerService.java @@ -18,7 +18,7 @@ package android.accounts; import android.Manifest; import android.app.ActivityManager; -import android.app.AppGlobals; +import android.app.ActivityManagerNative; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -32,10 +32,10 @@ import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.RegisteredServicesCache; import android.content.pm.RegisteredServicesCacheListener; import android.content.pm.UserInfo; -import android.content.res.Resources; import android.database.Cursor; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; @@ -55,10 +55,13 @@ import android.os.UserManager; import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import android.util.Slog; import android.util.SparseArray; import com.android.internal.R; import com.android.internal.util.IndentingPrintWriter; +import com.google.android.collect.Lists; +import com.google.android.collect.Sets; import java.io.File; import java.io.FileDescriptor; @@ -67,6 +70,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -243,8 +247,7 @@ public class AccountManagerService } public void systemReady() { - mAuthenticatorCache.generateServicesMap(); - initUser(0); + initUser(UserHandle.USER_OWNER); } private UserManager getUserManager() { @@ -261,7 +264,7 @@ public class AccountManagerService accounts = new UserAccounts(mContext, userId); mUsers.append(userId, accounts); purgeOldGrants(accounts); - validateAccountsAndPopulateCache(accounts); + validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */); } return accounts; } @@ -299,7 +302,34 @@ public class AccountManagerService } } - private void validateAccountsAndPopulateCache(UserAccounts accounts) { + /** + * Validate internal set of accounts against installed authenticators for + * given user. Clears cached authenticators before validating. + */ + public void validateAccounts(int userId) { + final UserAccounts accounts = getUserAccounts(userId); + + // Invalidate user-specific cache to make sure we catch any + // removed authenticators. + validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */); + } + + /** + * Validate internal set of accounts against installed authenticators for + * given user. Clear cached authenticators before validating when requested. + */ + private void validateAccountsInternal( + UserAccounts accounts, boolean invalidateAuthenticatorCache) { + if (invalidateAuthenticatorCache) { + mAuthenticatorCache.invalidateCache(accounts.userId); + } + + final HashSet<AuthenticatorDescription> knownAuth = Sets.newHashSet(); + for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : + mAuthenticatorCache.getAllServices(accounts.userId)) { + knownAuth.add(service.type); + } + synchronized (accounts.cacheLock) { final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); boolean accountDeleted = false; @@ -314,9 +344,9 @@ public class AccountManagerService final long accountId = cursor.getLong(0); final String accountType = cursor.getString(1); final String accountName = cursor.getString(2); - if (mAuthenticatorCache.getServiceInfo( - AuthenticatorDescription.newKey(accountType)) == null) { - Log.d(TAG, "deleting account " + accountName + " because type " + + if (!knownAuth.contains(AuthenticatorDescription.newKey(accountType))) { + Slog.w(TAG, "deleting account " + accountName + " because type " + accountType + " no longer has a registered authenticator"); db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null); accountDeleted = true; @@ -390,20 +420,10 @@ public class AccountManagerService } } - private List<UserInfo> getAllUsers() { - return getUserManager().getUsers(); - } - - public void onServiceChanged(AuthenticatorDescription desc, boolean removed) { - // Validate accounts for all users - List<UserInfo> users = getAllUsers(); - if (users == null) { - validateAccountsAndPopulateCache(getUserAccountsForCaller()); - } else { - for (UserInfo user : users) { - validateAccountsAndPopulateCache(getUserAccounts(user.id)); - } - } + @Override + public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) { + Slog.d(TAG, "onServiceChanged() for userId " + userId); + validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */); } public String getPassword(Account account) { @@ -470,10 +490,11 @@ public class AccountManagerService + "caller's uid " + Binder.getCallingUid() + ", pid " + Binder.getCallingPid()); } - long identityToken = clearCallingIdentity(); + final int userId = UserHandle.getCallingUserId(); + final long identityToken = clearCallingIdentity(); try { Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>> - authenticatorCollection = mAuthenticatorCache.getAllServices(); + authenticatorCollection = mAuthenticatorCache.getAllServices(userId); AuthenticatorDescription[] types = new AuthenticatorDescription[authenticatorCollection.size()]; int i = 0; @@ -1055,9 +1076,9 @@ public class AccountManagerService if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); checkBinderPermission(Manifest.permission.USE_CREDENTIALS); final UserAccounts accounts = getUserAccountsForCaller(); - AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo = - mAuthenticatorCache.getServiceInfo( - AuthenticatorDescription.newKey(account.type)); + final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; + authenticatorInfo = mAuthenticatorCache.getServiceInfo( + AuthenticatorDescription.newKey(account.type), accounts.userId); final boolean customTokens = authenticatorInfo != null && authenticatorInfo.type.customTokens; @@ -1074,7 +1095,7 @@ public class AccountManagerService if (notifyOnAuthFailure) { loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true); } - + long identityToken = clearCallingIdentity(); try { // if the caller has permission, do the peek. otherwise go the more expensive @@ -1183,28 +1204,6 @@ public class AccountManagerService account, authTokenType, uid), n, user); } - String getAccountLabel(String accountType) { - RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo = - mAuthenticatorCache.getServiceInfo( - AuthenticatorDescription.newKey(accountType)); - if (serviceInfo == null) { - throw new IllegalArgumentException("unknown account type: " + accountType); - } - - final Context authContext; - try { - authContext = mContext.createPackageContext( - serviceInfo.type.packageName, 0); - } catch (PackageManager.NameNotFoundException e) { - throw new IllegalArgumentException("unknown account type: " + accountType); - } - try { - return authContext.getString(serviceInfo.type.labelId); - } catch (Resources.NotFoundException e) { - throw new IllegalArgumentException("unknown account type: " + accountType); - } - } - private Intent newGrantCredentialsPermissionIntent(Account account, int uid, AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) { @@ -1506,28 +1505,48 @@ public class AccountManagerService } /** - * Returns all the accounts qualified by user. + * Returns accounts for all running users. + * * @hide */ + public AccountAndUser[] getRunningAccounts() { + final int[] runningUserIds; + try { + runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds(); + } catch (RemoteException e) { + // Running in system_server; should never happen + throw new RuntimeException(e); + } + return getAccounts(runningUserIds); + } + + /** {@hide} */ public AccountAndUser[] getAllAccounts() { - ArrayList<AccountAndUser> allAccounts = new ArrayList<AccountAndUser>(); - List<UserInfo> users = getAllUsers(); - if (users == null) return new AccountAndUser[0]; + final List<UserInfo> users = getUserManager().getUsers(); + final int[] userIds = new int[users.size()]; + for (int i = 0; i < userIds.length; i++) { + userIds[i] = users.get(i).id; + } + return getAccounts(userIds); + } - synchronized(mUsers) { - for (UserInfo user : users) { - UserAccounts userAccounts = getUserAccounts(user.id); + private AccountAndUser[] getAccounts(int[] userIds) { + final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList(); + synchronized (mUsers) { + for (int userId : userIds) { + UserAccounts userAccounts = getUserAccounts(userId); if (userAccounts == null) continue; synchronized (userAccounts.cacheLock) { Account[] accounts = getAccountsFromCacheLocked(userAccounts, null); for (int a = 0; a < accounts.length; a++) { - allAccounts.add(new AccountAndUser(accounts[a], user.id)); + runningAccounts.add(new AccountAndUser(accounts[a], userId)); } } } } - AccountAndUser[] accountsArray = new AccountAndUser[allAccounts.size()]; - return allAccounts.toArray(accountsArray); + + AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()]; + return runningAccounts.toArray(accountsArray); } public Account[] getAccounts(String type) { @@ -1836,9 +1855,9 @@ public class AccountManagerService * if no authenticator or the bind fails then return false, otherwise return true */ private boolean bindToAuthenticator(String authenticatorType) { - AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo = - mAuthenticatorCache.getServiceInfo( - AuthenticatorDescription.newKey(authenticatorType)); + final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; + authenticatorInfo = mAuthenticatorCache.getServiceInfo( + AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId); if (authenticatorInfo == null) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "there is no authenticator for " + authenticatorType @@ -2024,6 +2043,7 @@ public class AccountManagerService return false; } + @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { @@ -2033,17 +2053,15 @@ public class AccountManagerService return; } final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c"); + final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " "); - 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 List<UserInfo> users = getUserManager().getUsers(); + for (UserInfo user : users) { + ipw.println("User " + user + ":"); + ipw.increaseIndent(); + dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest); + ipw.println(); + ipw.decreaseIndent(); } } @@ -2083,7 +2101,7 @@ public class AccountManagerService } fout.println(); - mAuthenticatorCache.dump(fd, fout, args); + mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId); } } } @@ -2154,11 +2172,21 @@ public class AccountManagerService throw new SecurityException(msg); } - private boolean inSystemImage(int callerUid) { - String[] packages = mPackageManager.getPackagesForUid(callerUid); + private boolean inSystemImage(int callingUid) { + final int callingUserId = UserHandle.getUserId(callingUid); + + final PackageManager userPackageManager; + try { + userPackageManager = mContext.createPackageContextAsUser( + "android", 0, new UserHandle(callingUserId)).getPackageManager(); + } catch (NameNotFoundException e) { + return false; + } + + String[] packages = userPackageManager.getPackagesForUid(callingUid); for (String name : packages) { try { - PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */); + PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */); if (packageInfo != null && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { return true; @@ -2186,8 +2214,9 @@ public class AccountManagerService } private boolean hasAuthenticatorUid(String accountType, int callingUid) { + final int callingUserId = UserHandle.getUserId(callingUid); for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo : - mAuthenticatorCache.getAllServices()) { + mAuthenticatorCache.getAllServices(callingUserId)) { if (serviceInfo.type.type.equals(accountType)) { return (serviceInfo.uid == callingUid) || (mPackageManager.checkSignatures(serviceInfo.uid, callingUid) diff --git a/core/java/android/accounts/IAccountAuthenticatorCache.java b/core/java/android/accounts/IAccountAuthenticatorCache.java index 20dd585..06c2106 100644 --- a/core/java/android/accounts/IAccountAuthenticatorCache.java +++ b/core/java/android/accounts/IAccountAuthenticatorCache.java @@ -39,18 +39,19 @@ public interface IAccountAuthenticatorCache { * matches the account type or null if none is present */ RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> getServiceInfo( - AuthenticatorDescription type); + AuthenticatorDescription type, int userId); /** * @return A copy of a Collection of all the current Authenticators. */ - Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> getAllServices(); + Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> getAllServices( + int userId); /** * Dumps the state of the cache. See * {@link android.os.Binder#dump(java.io.FileDescriptor, java.io.PrintWriter, String[])} */ - void dump(FileDescriptor fd, PrintWriter fout, String[] args); + void dump(FileDescriptor fd, PrintWriter fout, String[] args, int userId); /** * Sets a listener that will be notified whenever the authenticator set changes @@ -61,8 +62,5 @@ public interface IAccountAuthenticatorCache { void setListener(RegisteredServicesCacheListener<AuthenticatorDescription> listener, Handler handler); - /** - * Refreshes the authenticator cache. - */ - void generateServicesMap(); -}
\ No newline at end of file + void invalidateCache(int userId); +} diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 0eda6b4..594be68 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1981,7 +1981,7 @@ public class ActivityManager { */ public boolean isUserRunning(int userid) { try { - return ActivityManagerNative.getDefault().isUserRunning(userid); + return ActivityManagerNative.getDefault().isUserRunning(userid, false); } catch (RemoteException e) { return false; } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index bb62c9e..7492629 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1608,7 +1608,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM case IS_USER_RUNNING_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); int userid = data.readInt(); - boolean result = isUserRunning(userid); + boolean orStopping = data.readInt() != 0; + boolean result = isUserRunning(userid, orStopping); reply.writeNoException(); reply.writeInt(result ? 1 : 0); return true; @@ -3865,11 +3866,12 @@ class ActivityManagerProxy implements IActivityManager return userInfo; } - public boolean isUserRunning(int userid) throws RemoteException { + public boolean isUserRunning(int userid, boolean orStopping) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeInt(userid); + data.writeInt(orStopping ? 1 : 0); mRemote.transact(IS_USER_RUNNING_TRANSACTION, data, reply, 0); reply.readException(); boolean result = reply.readInt() != 0; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index c324da9..3e1e358 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1352,10 +1352,16 @@ class ContextImpl extends Context { ComponentName cn = ActivityManagerNative.getDefault().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier()); - if (cn != null && cn.getPackageName().equals("!")) { - throw new SecurityException( - "Not allowed to start service " + service - + " without permission " + cn.getClassName()); + if (cn != null) { + if (cn.getPackageName().equals("!")) { + throw new SecurityException( + "Not allowed to start service " + service + + " without permission " + cn.getClassName()); + } else if (cn.getPackageName().equals("!!")) { + throw new SecurityException( + "Unable to start service " + service + + ": " + cn.getClassName()); + } } return cn; } catch (RemoteException e) { diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index da844ef..97250e9 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -326,7 +326,7 @@ public interface IActivityManager extends IInterface { public boolean switchUser(int userid) throws RemoteException; public int stopUser(int userid, IStopUserCallback callback) throws RemoteException; public UserInfo getCurrentUser() throws RemoteException; - public boolean isUserRunning(int userid) throws RemoteException; + public boolean isUserRunning(int userid, boolean orStopping) throws RemoteException; public int[] getRunningUserIds() throws RemoteException; public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException; diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index 6e2278d..6fdf3b4 100755 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -45,6 +45,7 @@ import java.util.List; public final class BluetoothA2dp implements BluetoothProfile { private static final String TAG = "BluetoothA2dp"; private static final boolean DBG = true; + private static final boolean VDBG = false; /** * Intent used to broadcast the change in connection state of the A2DP @@ -113,7 +114,7 @@ public final class BluetoothA2dp implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (DBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG,"Unbinding service..."); synchronized (mConnection) { try { mService = null; @@ -126,7 +127,7 @@ public final class BluetoothA2dp implements BluetoothProfile { synchronized (mConnection) { try { if (mService == null) { - if (DBG) Log.d(TAG,"Binding service..."); + if (VDBG) Log.d(TAG,"Binding service..."); if (!mContext.bindService(new Intent(IBluetoothA2dp.class.getName()), mConnection, 0)) { Log.e(TAG, "Could not bind to Bluetooth A2DP Service"); } @@ -269,7 +270,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * {@inheritDoc} */ public List<BluetoothDevice> getConnectedDevices() { - if (DBG) log("getConnectedDevices()"); + if (VDBG) log("getConnectedDevices()"); if (mService != null && isEnabled()) { try { return mService.getConnectedDevices(); @@ -286,7 +287,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * {@inheritDoc} */ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - if (DBG) log("getDevicesMatchingStates()"); + if (VDBG) log("getDevicesMatchingStates()"); if (mService != null && isEnabled()) { try { return mService.getDevicesMatchingConnectionStates(states); @@ -303,7 +304,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * {@inheritDoc} */ public int getConnectionState(BluetoothDevice device) { - if (DBG) log("getState(" + device + ")"); + if (VDBG) log("getState(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { try { @@ -365,7 +366,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ public int getPriority(BluetoothDevice device) { - if (DBG) log("getPriority(" + device + ")"); + if (VDBG) log("getPriority(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { try { diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index 541b69f..793d798 100755 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -46,6 +46,7 @@ import java.util.List; public final class BluetoothHeadset implements BluetoothProfile { private static final String TAG = "BluetoothHeadset"; private static final boolean DBG = true; + private static final boolean VDBG = false; /** * Intent used to broadcast the change in connection state of the Headset @@ -226,7 +227,7 @@ public final class BluetoothHeadset implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (DBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG,"Unbinding service..."); synchronized (mConnection) { try { mService = null; @@ -239,7 +240,7 @@ public final class BluetoothHeadset implements BluetoothProfile { synchronized (mConnection) { try { if (mService == null) { - if (DBG) Log.d(TAG,"Binding service..."); + if (VDBG) Log.d(TAG,"Binding service..."); if (!mContext.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) { Log.e(TAG, "Could not bind to Bluetooth Headset Service"); } @@ -281,7 +282,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * are ok. */ /*package*/ void close() { - if (DBG) log("close()"); + if (VDBG) log("close()"); IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { @@ -387,7 +388,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@inheritDoc} */ public List<BluetoothDevice> getConnectedDevices() { - if (DBG) log("getConnectedDevices()"); + if (VDBG) log("getConnectedDevices()"); if (mService != null && isEnabled()) { try { return mService.getConnectedDevices(); @@ -404,7 +405,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@inheritDoc} */ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - if (DBG) log("getDevicesMatchingStates()"); + if (VDBG) log("getDevicesMatchingStates()"); if (mService != null && isEnabled()) { try { return mService.getDevicesMatchingConnectionStates(states); @@ -421,7 +422,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@inheritDoc} */ public int getConnectionState(BluetoothDevice device) { - if (DBG) log("getConnectionState(" + device + ")"); + if (VDBG) log("getConnectionState(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { try { @@ -483,7 +484,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public int getPriority(BluetoothDevice device) { - if (DBG) log("getPriority(" + device + ")"); + if (VDBG) log("getPriority(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { try { @@ -566,7 +567,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * false otherwise or on error */ public boolean isAudioConnected(BluetoothDevice device) { - if (DBG) log("isAudioConnected()"); + if (VDBG) log("isAudioConnected()"); if (mService != null && isEnabled() && isValidDevice(device)) { try { @@ -594,7 +595,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public int getBatteryUsageHint(BluetoothDevice device) { - if (DBG) log("getBatteryUsageHint()"); + if (VDBG) log("getBatteryUsageHint()"); if (mService != null && isEnabled() && isValidDevice(device)) { try { @@ -661,7 +662,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public int getAudioState(BluetoothDevice device) { - if (DBG) log("getAudioState"); + if (VDBG) log("getAudioState"); if (mService != null && !isDisabled()) { try { return mService.getAudioState(device); @@ -683,7 +684,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ public boolean isAudioOn() { - if (DBG) log("isAudioOn()"); + if (VDBG) log("isAudioOn()"); if (mService != null && isEnabled()) { try { return mService.isAudioOn(); diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java index 4a0bc7e..cb23662 100644 --- a/core/java/android/bluetooth/BluetoothHealth.java +++ b/core/java/android/bluetooth/BluetoothHealth.java @@ -58,6 +58,7 @@ import java.util.List; public final class BluetoothHealth implements BluetoothProfile { private static final String TAG = "BluetoothHealth"; private static final boolean DBG = true; + private static final boolean VDBG = false; /** * Health Profile Source Role - the health device. @@ -102,7 +103,7 @@ public final class BluetoothHealth implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (DBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG,"Unbinding service..."); synchronized (mConnection) { try { mService = null; @@ -115,7 +116,7 @@ public final class BluetoothHealth implements BluetoothProfile { synchronized (mConnection) { try { if (mService == null) { - if (DBG) Log.d(TAG,"Binding service..."); + if (VDBG) Log.d(TAG,"Binding service..."); if (!mContext.bindService(new Intent(IBluetoothHealth.class.getName()), mConnection, 0)) { Log.e(TAG, "Could not bind to Bluetooth Health Service"); } @@ -148,7 +149,7 @@ public final class BluetoothHealth implements BluetoothProfile { BluetoothHealthCallback callback) { if (!isEnabled() || name == null) return false; - if (DBG) log("registerSinkApplication(" + name + ":" + dataType + ")"); + if (VDBG) log("registerSinkApplication(" + name + ":" + dataType + ")"); return registerAppConfiguration(name, dataType, SINK_ROLE, CHANNEL_TYPE_ANY, callback); } @@ -174,7 +175,7 @@ public final class BluetoothHealth implements BluetoothProfile { boolean result = false; if (!isEnabled() || !checkAppParam(name, role, channelType, callback)) return result; - if (DBG) log("registerApplication(" + name + ":" + dataType + ")"); + if (VDBG) log("registerApplication(" + name + ":" + dataType + ")"); BluetoothHealthCallbackWrapper wrapper = new BluetoothHealthCallbackWrapper(callback); BluetoothHealthAppConfiguration config = new BluetoothHealthAppConfiguration(name, dataType, role, channelType); @@ -488,7 +489,7 @@ public final class BluetoothHealth implements BluetoothProfile { } /*package*/ void close() { - if (DBG) log("close()"); + if (VDBG) log("close()"); IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { try { diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothInputDevice.java index bff966d..db7e424 100755 --- a/core/java/android/bluetooth/BluetoothInputDevice.java +++ b/core/java/android/bluetooth/BluetoothInputDevice.java @@ -45,6 +45,7 @@ import java.util.List; public final class BluetoothInputDevice implements BluetoothProfile { private static final String TAG = "BluetoothInputDevice"; private static final boolean DBG = true; + private static final boolean VDBG = false; /** * Intent used to broadcast the change in connection state of the Input @@ -191,7 +192,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (DBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG,"Unbinding service..."); synchronized (mConnection) { try { mService = null; @@ -204,7 +205,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { synchronized (mConnection) { try { if (mService == null) { - if (DBG) Log.d(TAG,"Binding service..."); + if (VDBG) Log.d(TAG,"Binding service..."); if (!mContext.bindService(new Intent(IBluetoothInputDevice.class.getName()), mConnection, 0)) { Log.e(TAG, "Could not bind to Bluetooth HID Service"); } @@ -243,7 +244,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { } /*package*/ void close() { - if (DBG) log("close()"); + if (VDBG) log("close()"); IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { try { @@ -344,7 +345,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * {@inheritDoc} */ public List<BluetoothDevice> getConnectedDevices() { - if (DBG) log("getConnectedDevices()"); + if (VDBG) log("getConnectedDevices()"); if (mService != null && isEnabled()) { try { return mService.getConnectedDevices(); @@ -361,7 +362,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * {@inheritDoc} */ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - if (DBG) log("getDevicesMatchingStates()"); + if (VDBG) log("getDevicesMatchingStates()"); if (mService != null && isEnabled()) { try { return mService.getDevicesMatchingConnectionStates(states); @@ -378,7 +379,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * {@inheritDoc} */ public int getConnectionState(BluetoothDevice device) { - if (DBG) log("getState(" + device + ")"); + if (VDBG) log("getState(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getConnectionState(device); @@ -438,7 +439,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * @hide */ public int getPriority(BluetoothDevice device) { - if (DBG) log("getPriority(" + device + ")"); + if (VDBG) log("getPriority(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getPriority(device); @@ -519,7 +520,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * @hide */ public boolean getProtocolMode(BluetoothDevice device) { - if (DBG) log("getProtocolMode(" + device + ")"); + if (VDBG) log("getProtocolMode(" + device + ")"); if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getProtocolMode(device); @@ -570,7 +571,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * @hide */ public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) { - if (DBG) log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + "bufferSize=" + bufferSize); + if (VDBG) log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + "bufferSize=" + bufferSize); if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.getReport(device, reportType, reportId, bufferSize); diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java index cae7a73..e25ec86 100644 --- a/core/java/android/bluetooth/BluetoothPan.java +++ b/core/java/android/bluetooth/BluetoothPan.java @@ -44,6 +44,7 @@ import java.util.List; public final class BluetoothPan implements BluetoothProfile { private static final String TAG = "BluetoothPan"; private static final boolean DBG = true; + private static final boolean VDBG = false; /** * Intent used to broadcast the change in connection state of the Pan @@ -145,7 +146,7 @@ public final class BluetoothPan implements BluetoothProfile { } /*package*/ void close() { - if (DBG) log("close()"); + if (VDBG) log("close()"); if (mConnection != null) { mContext.unbindService(mConnection); mConnection = null; @@ -175,7 +176,7 @@ public final class BluetoothPan implements BluetoothProfile { } Log.d(TAG, "BluetoothPan(), bindService called"); } else { - if (DBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG,"Unbinding service..."); synchronized (mConnection) { try { mPanService = null; @@ -266,7 +267,7 @@ public final class BluetoothPan implements BluetoothProfile { * {@inheritDoc} */ public List<BluetoothDevice> getConnectedDevices() { - if (DBG) log("getConnectedDevices()"); + if (VDBG) log("getConnectedDevices()"); if (mPanService != null && isEnabled()) { try { return mPanService.getConnectedDevices(); @@ -283,7 +284,7 @@ public final class BluetoothPan implements BluetoothProfile { * {@inheritDoc} */ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - if (DBG) log("getDevicesMatchingStates()"); + if (VDBG) log("getDevicesMatchingStates()"); if (mPanService != null && isEnabled()) { try { return mPanService.getDevicesMatchingConnectionStates(states); @@ -300,7 +301,7 @@ public final class BluetoothPan implements BluetoothProfile { * {@inheritDoc} */ public int getConnectionState(BluetoothDevice device) { - if (DBG) log("getState(" + device + ")"); + if (VDBG) log("getState(" + device + ")"); if (mPanService != null && isEnabled() && isValidDevice(device)) { try { @@ -324,7 +325,7 @@ public final class BluetoothPan implements BluetoothProfile { } public boolean isTetheringOn() { - if (DBG) log("isTetheringOn()"); + if (VDBG) log("isTetheringOn()"); try { return mPanService.isTetheringOn(); } catch (RemoteException e) { diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java index 7de2ef6..b5280e5 100755 --- a/core/java/android/bluetooth/BluetoothPbap.java +++ b/core/java/android/bluetooth/BluetoothPbap.java @@ -51,7 +51,8 @@ import android.util.Log; public class BluetoothPbap { private static final String TAG = "BluetoothPbap"; - private static final boolean DBG = false; + private static final boolean DBG = true; + private static final boolean VDBG = false; /** int extra for PBAP_STATE_CHANGED_ACTION */ public static final String PBAP_STATE = @@ -114,7 +115,7 @@ public class BluetoothPbap { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (DBG) Log.d(TAG,"Unbinding service..."); + if (VDBG) Log.d(TAG,"Unbinding service..."); synchronized (mConnection) { try { mService = null; @@ -127,7 +128,7 @@ public class BluetoothPbap { synchronized (mConnection) { try { if (mService == null) { - if (DBG) Log.d(TAG,"Binding service..."); + if (VDBG) Log.d(TAG,"Binding service..."); if (!mContext.bindService( new Intent(IBluetoothPbap.class.getName()), mConnection, 0)) { @@ -206,7 +207,7 @@ public class BluetoothPbap { * object is currently not connected to the Pbap service. */ public int getState() { - if (DBG) log("getState()"); + if (VDBG) log("getState()"); if (mService != null) { try { return mService.getState(); @@ -225,7 +226,7 @@ public class BluetoothPbap { * the Pbap service. */ public BluetoothDevice getClient() { - if (DBG) log("getClient()"); + if (VDBG) log("getClient()"); if (mService != null) { try { return mService.getClient(); @@ -243,7 +244,7 @@ public class BluetoothPbap { * object is not currently connected to the Pbap service. */ public boolean isConnected(BluetoothDevice device) { - if (DBG) log("isConnected(" + device + ")"); + if (VDBG) log("isConnected(" + device + ")"); if (mService != null) { try { return mService.isConnected(device); diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index 1bc640f..aba8710 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -72,6 +72,8 @@ import java.nio.ByteBuffer; */ public final class BluetoothSocket implements Closeable { private static final String TAG = "BluetoothSocket"; + private static final boolean DBG = true; + private static final boolean VDBG = false; /** @hide */ public static final int MAX_RFCOMM_CHANNEL = 30; @@ -172,7 +174,7 @@ public final class BluetoothSocket implements Closeable { BluetoothSocket as = new BluetoothSocket(this); as.mSocketState = SocketState.CONNECTED; FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors(); - Log.d(TAG, "socket fd passed by stack fds: " + fds); + if (VDBG) Log.d(TAG, "socket fd passed by stack fds: " + fds); if(fds == null || fds.length != 1) { Log.e(TAG, "socket fd passed from stack failed, fds: " + fds); throw new IOException("bt socket acept failed"); @@ -291,7 +293,7 @@ public final class BluetoothSocket implements Closeable { mUuid, mPort, getSecurityFlags()); synchronized(this) { - Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd); + if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd); if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); if (mPfd == null) throw new IOException("bt socket connect failed"); FileDescriptor fd = mPfd.getFileDescriptor(); @@ -339,23 +341,24 @@ public final class BluetoothSocket implements Closeable { // read out port number try { synchronized(this) { - Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + mPfd); + if (VDBG) Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + + mPfd); if(mSocketState != SocketState.INIT) return EBADFD; if(mPfd == null) return -1; FileDescriptor fd = mPfd.getFileDescriptor(); - Log.d(TAG, "bindListen(), new LocalSocket "); + if (VDBG) Log.d(TAG, "bindListen(), new LocalSocket "); mSocket = new LocalSocket(fd); - Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() "); + if (VDBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() "); mSocketIS = mSocket.getInputStream(); mSocketOS = mSocket.getOutputStream(); } - Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS); + if (VDBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS); int channel = readInt(mSocketIS); synchronized(this) { if(mSocketState == SocketState.INIT) mSocketState = SocketState.LISTENING; } - Log.d(TAG, "channel: " + channel); + if (VDBG) Log.d(TAG, "channel: " + channel); if (mPort == -1) { mPort = channel; } // else ASSERT(mPort == channel) @@ -385,26 +388,26 @@ public final class BluetoothSocket implements Closeable { } /*package*/ int available() throws IOException { - Log.d(TAG, "available: " + mSocketIS); + if (VDBG) Log.d(TAG, "available: " + mSocketIS); return mSocketIS.available(); } /*package*/ int read(byte[] b, int offset, int length) throws IOException { - Log.d(TAG, "read in: " + mSocketIS + " len: " + length); + if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length); int ret = mSocketIS.read(b, offset, length); if(ret < 0) throw new IOException("bt socket closed, read return: " + ret); - Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret); + if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret); return ret; } /*package*/ int write(byte[] b, int offset, int length) throws IOException { - Log.d(TAG, "write: " + mSocketOS + " length: " + length); + if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length); mSocketOS.write(b, offset, length); // There is no good way to confirm since the entire process is asynchronous anyway - Log.d(TAG, "write out: " + mSocketOS + " length: " + length); + if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length); return length; } @@ -420,10 +423,10 @@ public final class BluetoothSocket implements Closeable { if(mSocketState == SocketState.CLOSED) return; mSocketState = SocketState.CLOSED; - Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS + + if (VDBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS + "mSocket: " + mSocket); if(mSocket != null) { - Log.d(TAG, "Closing mSocket: " + mSocket); + if (VDBG) Log.d(TAG, "Closing mSocket: " + mSocket); mSocket.shutdownInput(); mSocket.shutdownOutput(); mSocket.close(); @@ -449,7 +452,7 @@ public final class BluetoothSocket implements Closeable { private String waitSocketSignal(InputStream is) throws IOException { byte [] sig = new byte[SOCK_SIGNAL_SIZE]; int ret = readAll(is, sig); - Log.d(TAG, "waitSocketSignal read 16 bytes signal ret: " + ret); + if (VDBG) Log.d(TAG, "waitSocketSignal read 16 bytes signal ret: " + ret); ByteBuffer bb = ByteBuffer.wrap(sig); bb.order(ByteOrder.nativeOrder()); int size = bb.getShort(); @@ -458,7 +461,7 @@ public final class BluetoothSocket implements Closeable { int channel = bb.getInt(); int status = bb.getInt(); String RemoteAddr = convertAddr(addr); - Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: " + if (VDBG) Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: " + RemoteAddr + ", channel: " + channel + ", status: " + status); if(status != 0) throw new IOException("Connection failure, status: " + status); @@ -481,7 +484,7 @@ public final class BluetoothSocket implements Closeable { private int readInt(InputStream is) throws IOException { byte[] ibytes = new byte[4]; int ret = readAll(is, ibytes); - Log.d(TAG, "inputStream.read ret: " + ret); + if (VDBG) Log.d(TAG, "inputStream.read ret: " + ret); ByteBuffer bb = ByteBuffer.wrap(ibytes); bb.order(ByteOrder.nativeOrder()); return bb.getInt(); diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java index 30406e9..063e5a8 100644 --- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -51,6 +51,8 @@ import java.util.concurrent.atomic.AtomicInteger; public class BluetoothTetheringDataTracker implements NetworkStateTracker { private static final String NETWORKTYPE = "BLUETOOTH_TETHER"; private static final String TAG = "BluetoothTethering"; + private static final boolean DBG = true; + private static final boolean VDBG = false; private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); @@ -99,10 +101,10 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { * Begin monitoring connectivity */ public void startMonitoring(Context context, Handler target) { - Log.d(TAG, "startMonitoring: target: " + target); + if (DBG) Log.d(TAG, "startMonitoring: target: " + target); mContext = context; mCsHandler = target; - Log.d(TAG, "startMonitoring: mCsHandler: " + mCsHandler); + if (VDBG) Log.d(TAG, "startMonitoring: mCsHandler: " + mCsHandler); BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter != null) { adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN); @@ -310,31 +312,31 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { } public synchronized void startReverseTether(String iface) { mIface = iface; - Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); + if (DBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); mDhcpThread = new Thread(new Runnable() { public void run() { //TODO(): Add callbacks for failure and success case. //Currently this thread runs independently. - Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); + if (DBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); String DhcpResultName = "dhcp." + mIface + ".result";; String result = ""; - Log.d(TAG, "waiting for change of sys prop dhcp result: " + DhcpResultName); + if (VDBG) Log.d(TAG, "waiting for change of sys prop dhcp result: " + DhcpResultName); for(int i = 0; i < 30*5; i++) { try { Thread.sleep(200); } catch (InterruptedException ie) { return;} result = SystemProperties.get(DhcpResultName); - Log.d(TAG, "read " + DhcpResultName + ": " + result); + if (VDBG) Log.d(TAG, "read " + DhcpResultName + ": " + result); if(result.equals("failed")) { Log.e(TAG, "startReverseTether, failed to start dhcp service"); return; } if(result.equals("ok")) { - Log.d(TAG, "startReverseTether, dhcp resut: " + result); + if (VDBG) Log.d(TAG, "startReverseTether, dhcp resut: " + result); if(readLinkProperty(mIface)) { mNetworkInfo.setIsAvailable(true); mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); - Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); + if (VDBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); if(mCsHandler != null) { Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); msg.sendToTarget(); @@ -346,7 +348,7 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { return; } } - Log.d(TAG, "startReverseTether, dhcp failed, resut: " + result); + Log.e(TAG, "startReverseTether, dhcp failed, resut: " + result); } }); mDhcpThread.start(); diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java index 0f6488a..4512e82 100644 --- a/core/java/android/content/ContentService.java +++ b/core/java/android/content/ContentService.java @@ -345,10 +345,11 @@ public final class ContentService extends IContentService.Stub { public SyncAdapterType[] getSyncAdapterTypes() { // This makes it so that future permission checks will be in the context of this // process rather than the caller's process. We will restore this before returning. - long identityToken = clearCallingIdentity(); + final int userId = UserHandle.getCallingUserId(); + final long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); - return syncManager.getSyncAdapterTypes(); + return syncManager.getSyncAdapterTypes(userId); } finally { restoreCallingIdentity(identityToken); } diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 564a804..93c9526 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -20,8 +20,8 @@ import android.accounts.Account; import android.accounts.AccountAndUser; import android.accounts.AccountManager; import android.accounts.AccountManagerService; -import android.accounts.OnAccountsUpdateListener; import android.app.ActivityManager; +import android.app.ActivityManagerNative; import android.app.AlarmManager; import android.app.Notification; import android.app.NotificationManager; @@ -58,6 +58,7 @@ import android.util.Log; import android.util.Pair; import com.android.internal.R; +import com.android.internal.util.IndentingPrintWriter; import com.google.android.collect.Lists; import com.google.android.collect.Maps; import com.google.android.collect.Sets; @@ -81,7 +82,7 @@ import java.util.concurrent.CountDownLatch; /** * @hide */ -public class SyncManager implements OnAccountsUpdateListener { +public class SyncManager { private static final String TAG = "SyncManager"; /** Delay a sync due to local changes this long. In milliseconds */ @@ -141,7 +142,8 @@ public class SyncManager implements OnAccountsUpdateListener { private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0]; - private volatile AccountAndUser[] mAccounts = INITIAL_ACCOUNTS_ARRAY; + // TODO: add better locking around mRunningAccounts + private volatile AccountAndUser[] mRunningAccounts = INITIAL_ACCOUNTS_ARRAY; volatile private PowerManager.WakeLock mHandleAlarmWakeLock; volatile private PowerManager.WakeLock mSyncManagerWakeLock; @@ -205,7 +207,10 @@ public class SyncManager implements OnAccountsUpdateListener { private BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { - onAccountsUpdated(null); + updateRunningAccounts(); + + // Kick off sync for everyone, since this was a radical account change + scheduleSync(null, UserHandle.USER_ALL, null, null, 0 /* no delay */, false); } }; @@ -242,33 +247,14 @@ public class SyncManager implements OnAccountsUpdateListener { 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; - - List<UserInfo> users = getAllUsers(); - if (users == null) return; - - int count = 0; - - // Get accounts from AccountManager for all the users on the system - // TODO: Limit this to active users, when such a concept exists. - AccountAndUser[] allAccounts = AccountManagerService.getSingleton().getAllAccounts(); - for (UserInfo user : users) { - if (mBootCompleted) { - Account[] accountsForUser = - AccountManagerService.getSingleton().getAccounts(user.id); - mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id); - } - } - - mAccounts = allAccounts; + public void updateRunningAccounts() { + mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts(); for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) { - if (!containsAccountAndUser(allAccounts, + if (!containsAccountAndUser(mRunningAccounts, currentSyncContext.mSyncOperation.account, currentSyncContext.mSyncOperation.userId)) { - Log.d(TAG, "canceling sync since the account has been removed"); + Log.d(TAG, "canceling sync since the account is no longer running"); sendSyncFinishedOrCanceledMessage(currentSyncContext, null /* no result since this is a cancel */); } @@ -277,26 +263,6 @@ public class SyncManager implements OnAccountsUpdateListener { // we must do this since we don't bother scheduling alarms when // the accounts are not set yet sendCheckAlarmsMessage(); - - 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 - // has a change. - // - // If there was a real account change then force a sync of all accounts. - // This is a bit of overkill, but at least it will end up retrying syncs - // that failed due to an authentication failure and thus will recover if the - // account change was a password update. - // - // If this was the bootup case then don't sync everything, instead only - // sync those that have an unknown syncable state, which will give them - // a chance to set their syncable state. - - boolean onlyThoseWithUnkownSyncableState = justBootedUp; - scheduleSync(null, UserHandle.USER_ALL, null, null, 0 /* no delay */, - onlyThoseWithUnkownSyncableState); - } } private BroadcastReceiver mConnectivityIntentReceiver = @@ -336,19 +302,18 @@ public class SyncManager implements OnAccountsUpdateListener { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); + if (userId == UserHandle.USER_NULL) return; + if (Intent.ACTION_USER_REMOVED.equals(action)) { - Log.i(TAG, "User removed - cleanup: u" + userId); - onUserRemoved(intent); - } else if (Intent.ACTION_USER_STARTED.equals(action)) { - Log.i(TAG, "User started - check alarms: u" + userId); - sendCheckAlarmsMessage(); - } else if (Intent.ACTION_USER_STOPPED.equals(action)) { - Log.i(TAG, "User stopped - stop syncs: u" + userId); - cancelActiveSync( - null /* any account */, - userId, - null /* any authority */); + Log.i(TAG, "User removed: u" + userId); + onUserRemoved(userId); + } else if (Intent.ACTION_USER_STARTING.equals(action)) { + Log.i(TAG, "User starting: u" + userId); + onUserStarting(userId); + } else if (Intent.ACTION_USER_STOPPING.equals(action)) { + Log.i(TAG, "User stopping: u" + userId); + onUserStopping(userId); } } }; @@ -390,7 +355,8 @@ public class SyncManager implements OnAccountsUpdateListener { mSyncHandler = new SyncHandler(syncThread.getLooper()); mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() { - public void onServiceChanged(SyncAdapterType type, boolean removed) { + @Override + public void onServiceChanged(SyncAdapterType type, int userId, boolean removed) { if (!removed) { scheduleSync(null, UserHandle.USER_ALL, type.authority, null, 0 /* no delay */, false /* onlyThoseWithUnkownSyncableState */); @@ -422,7 +388,8 @@ public class SyncManager implements OnAccountsUpdateListener { intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_USER_REMOVED); - intentFilter.addAction(Intent.ACTION_USER_STARTED); + intentFilter.addAction(Intent.ACTION_USER_STARTING); + intentFilter.addAction(Intent.ACTION_USER_STOPPING); mContext.registerReceiverAsUser( mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null); @@ -467,8 +434,9 @@ public class SyncManager implements OnAccountsUpdateListener { UserHandle.ALL, new IntentFilter(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION), null, null); + // do this synchronously to ensure we have the accounts before this call returns - onAccountsUpdated(null); + onUserStarting(UserHandle.USER_OWNER); } // Pick a random second in a day to seed all periodic syncs @@ -548,7 +516,7 @@ public class SyncManager implements OnAccountsUpdateListener { } else { // if the accounts aren't configured yet then we can't support an account-less // sync request - accounts = mAccounts; + accounts = mRunningAccounts; if (accounts.length == 0) { if (isLoggable) { Log.v(TAG, "scheduleSync: no accounts configured, dropping"); @@ -579,32 +547,33 @@ public class SyncManager implements OnAccountsUpdateListener { source = SyncStorageEngine.SOURCE_SERVER; } - // Compile a list of authorities that have sync adapters. - // For each authority sync each account that matches a sync adapter. - final HashSet<String> syncableAuthorities = new HashSet<String>(); - for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter : - mSyncAdapters.getAllServices()) { - syncableAuthorities.add(syncAdapter.type.authority); - } + for (AccountAndUser account : accounts) { + // Compile a list of authorities that have sync adapters. + // For each authority sync each account that matches a sync adapter. + final HashSet<String> syncableAuthorities = new HashSet<String>(); + for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter : + mSyncAdapters.getAllServices(account.userId)) { + syncableAuthorities.add(syncAdapter.type.authority); + } - // if the url was specified then replace the list of authorities with just this authority - // or clear it if this authority isn't syncable - if (requestedAuthority != null) { - final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority); - syncableAuthorities.clear(); - if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority); - } + // if the url was specified then replace the list of authorities + // with just this authority or clear it if this authority isn't + // syncable + if (requestedAuthority != null) { + final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority); + syncableAuthorities.clear(); + if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority); + } - for (String authority : syncableAuthorities) { - for (AccountAndUser account : accounts) { + for (String authority : syncableAuthorities) { int isSyncable = mSyncStorageEngine.getIsSyncable(account.account, account.userId, authority); if (isSyncable == 0) { continue; } - final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo = - mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey(authority, account.account.type)); + final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; + syncAdapterInfo = mSyncAdapters.getServiceInfo( + SyncAdapterType.newKey(authority, account.account.type), account.userId); if (syncAdapterInfo == null) { continue; } @@ -681,10 +650,9 @@ public class SyncManager implements OnAccountsUpdateListener { false /* onlyThoseWithUnkownSyncableState */); } - public SyncAdapterType[] getSyncAdapterTypes() { - final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> - serviceInfos = - mSyncAdapters.getAllServices(); + public SyncAdapterType[] getSyncAdapterTypes(int userId) { + final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos; + serviceInfos = mSyncAdapters.getAllServices(userId); SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()]; int i = 0; for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) { @@ -920,16 +888,42 @@ public class SyncManager implements OnAccountsUpdateListener { } } - private void onUserRemoved(Intent intent) { - int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); - if (userId == -1) return; - removeUser(userId); + private void onUserStarting(int userId) { + // Make sure that accounts we're about to use are valid + AccountManagerService.getSingleton().validateAccounts(userId); + + mSyncAdapters.invalidateCache(userId); + + updateRunningAccounts(); + + final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId); + mSyncStorageEngine.doDatabaseCleanup(accounts, userId); + + mSyncQueue.addPendingOperations(userId); + + // Schedule sync for any accounts under started user + for (Account account : accounts) { + scheduleSync(account, userId, null, null, 0 /* no delay */, + true /* onlyThoseWithUnknownSyncableState */); + } + + sendCheckAlarmsMessage(); + } + + private void onUserStopping(int userId) { + updateRunningAccounts(); + + cancelActiveSync( + null /* any account */, + userId, + null /* any authority */); } - private void removeUser(int userId) { + private void onUserRemoved(int userId) { + updateRunningAccounts(); + // Clean up the storage engine database mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId); - onAccountsUpdated(null); synchronized (mSyncQueue) { mSyncQueue.removeUser(userId); } @@ -1062,14 +1056,10 @@ public class SyncManager implements OnAccountsUpdateListener { } protected void dump(FileDescriptor fd, PrintWriter pw) { - dumpSyncState(pw); - dumpSyncHistory(pw); - - pw.println(); - pw.println("SyncAdapters:"); - for (RegisteredServicesCache.ServiceInfo info : mSyncAdapters.getAllServices()) { - pw.println(" " + info); - } + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + dumpSyncState(ipw); + dumpSyncHistory(ipw); + dumpSyncAdapters(ipw); } static String formatTime(long time) { @@ -1085,13 +1075,13 @@ public class SyncManager implements OnAccountsUpdateListener { if (users != null) { for (UserInfo user : users) { pw.print("u" + user.id + "=" - + mSyncStorageEngine.getMasterSyncAutomatically(user.id)); + + mSyncStorageEngine.getMasterSyncAutomatically(user.id) + " "); } pw.println(); } pw.print("memory low: "); pw.println(mStorageIsLow); - final AccountAndUser[] accounts = mAccounts; + final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts(); pw.print("accounts: "); if (accounts != INITIAL_ACCOUNTS_ARRAY) { @@ -1153,7 +1143,7 @@ public class SyncManager implements OnAccountsUpdateListener { pw.print(" "); pw.print(account.account.type); pw.println(":"); for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType : - mSyncAdapters.getAllServices()) { + mSyncAdapters.getAllServices(account.userId)) { if (!syncAdapterType.type.accountType.equals(account.account.type)) { continue; } @@ -1530,6 +1520,23 @@ public class SyncManager implements OnAccountsUpdateListener { } } + private void dumpSyncAdapters(IndentingPrintWriter pw) { + pw.println(); + final List<UserInfo> users = getAllUsers(); + if (users != null) { + for (UserInfo user : users) { + pw.println("Sync adapters for " + user + ":"); + pw.increaseIndent(); + for (RegisteredServicesCache.ServiceInfo<?> info : + mSyncAdapters.getAllServices(user.id)) { + pw.println(info); + } + pw.decreaseIndent(); + pw.println(); + } + } + } + private static class AuthoritySyncStats { String name; long elapsedTime; @@ -1613,18 +1620,10 @@ public class SyncManager implements OnAccountsUpdateListener { Maps.newHashMap(); private volatile CountDownLatch mReadyToRunLatch = new CountDownLatch(1); + public void onBootCompleted() { mBootCompleted = true; - // 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(); } @@ -1814,7 +1813,7 @@ public class SyncManager implements OnAccountsUpdateListener { return earliestFuturePollTime; } - AccountAndUser[] accounts = mAccounts; + AccountAndUser[] accounts = mRunningAccounts; final long nowAbsolute = System.currentTimeMillis(); final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis) @@ -1869,9 +1868,10 @@ public class SyncManager implements OnAccountsUpdateListener { // Sync now 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)); + final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; + syncAdapterInfo = mSyncAdapters.getServiceInfo( + SyncAdapterType.newKey(info.authority, info.account.type), + info.userId); if (syncAdapterInfo == null) { continue; } @@ -1927,7 +1927,7 @@ public class SyncManager implements OnAccountsUpdateListener { // If the accounts aren't known yet then we aren't ready to run. We will be kicked // when the account lookup request does complete. - AccountAndUser[] accounts = mAccounts; + AccountAndUser[] accounts = mRunningAccounts; if (accounts == INITIAL_ACCOUNTS_ARRAY) { if (isLoggable) { Log.v(TAG, "maybeStartNextSync: accounts not known, skipping"); @@ -1998,7 +1998,7 @@ public class SyncManager implements OnAccountsUpdateListener { final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; syncAdapterInfo = mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey(op.authority, op.account.type)); + SyncAdapterType.newKey(op.authority, op.account.type), op.userId); // only proceed if network is connected for requesting UID final boolean uidNetworkConnected; @@ -2030,7 +2030,7 @@ public class SyncManager implements OnAccountsUpdateListener { for (Integer user : removedUsers) { // if it's still removed if (mUserManager.getUserInfo(user) == null) { - removeUser(user); + onUserRemoved(user); } } } @@ -2167,8 +2167,8 @@ public class SyncManager implements OnAccountsUpdateListener { // connect to the sync adapter SyncAdapterType syncAdapterType = SyncAdapterType.newKey(op.authority, op.account.type); - RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo = - mSyncAdapters.getServiceInfo(syncAdapterType); + final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; + syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, op.userId); if (syncAdapterInfo == null) { Log.d(TAG, "can't find a sync adapter for " + syncAdapterType + ", removing settings for it"); diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java index c18c86b..395658c 100644 --- a/core/java/android/content/SyncQueue.java +++ b/core/java/android/content/SyncQueue.java @@ -16,14 +16,15 @@ package android.content; -import com.google.android.collect.Maps; - -import android.content.pm.RegisteredServicesCache; +import android.accounts.Account; +import android.content.pm.RegisteredServicesCache.ServiceInfo; import android.os.SystemClock; +import android.os.UserHandle; import android.text.format.DateUtils; -import android.util.Pair; import android.util.Log; -import android.accounts.Account; +import android.util.Pair; + +import com.google.android.collect.Maps; import java.util.ArrayList; import java.util.HashMap; @@ -36,7 +37,9 @@ import java.util.Map; */ public class SyncQueue { private static final String TAG = "SyncManager"; - private SyncStorageEngine mSyncStorageEngine; + + private final SyncStorageEngine mSyncStorageEngine; + private final SyncAdaptersCache mSyncAdapters; // A Map of SyncOperations operationKey -> SyncOperation that is designed for // quick lookup of an enqueued SyncOperation. @@ -44,23 +47,28 @@ public class SyncQueue { public SyncQueue(SyncStorageEngine syncStorageEngine, final SyncAdaptersCache syncAdapters) { mSyncStorageEngine = syncStorageEngine; - ArrayList<SyncStorageEngine.PendingOperation> ops - = mSyncStorageEngine.getPendingOperations(); - 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.userId, op.authority); - final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo = - syncAdapters.getServiceInfo( - SyncAdapterType.newKey(op.authority, op.account.type)); + mSyncAdapters = syncAdapters; + + addPendingOperations(UserHandle.USER_OWNER); + } + + public void addPendingOperations(int userId) { + for (SyncStorageEngine.PendingOperation op : mSyncStorageEngine.getPendingOperations()) { + if (op.userId != userId) continue; + + final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff( + op.account, op.userId, op.authority); + final ServiceInfo<SyncAdapterType> syncAdapterInfo = mSyncAdapters.getServiceInfo( + SyncAdapterType.newKey(op.authority, op.account.type), op.userId); if (syncAdapterInfo == null) { + Log.w(TAG, "Missing sync adapter info for authority " + op.authority + ", userId " + + op.userId); continue; } SyncOperation syncOperation = new SyncOperation( op.account, op.userId, op.syncSource, op.authority, op.extras, 0 /* delay */, backoff != null ? backoff.first : 0, - syncStorageEngine.getDelayUntilTime(op.account, op.userId, op.authority), + mSyncStorageEngine.getDelayUntilTime(op.account, op.userId, op.authority), syncAdapterInfo.type.allowParallelSyncs()); syncOperation.expedited = op.expedited; syncOperation.pendingOperation = op; diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index 7642670..0b91786 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -16,49 +16,54 @@ package android.content.pm; -import android.content.Context; import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.ComponentName; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.os.Environment; import android.os.Handler; +import android.os.UserHandle; import android.util.AtomicFile; -import android.util.Log; import android.util.AttributeSet; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; import android.util.Xml; -import java.util.Map; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicReference; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.io.IOException; -import java.io.FileInputStream; - import com.android.internal.util.FastXmlSerializer; - -import com.google.android.collect.Maps; import com.google.android.collect.Lists; +import com.google.android.collect.Maps; -import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + /** - * A cache of registered services. This cache - * is built by interrogating the {@link PackageManager} and is updated as packages are added, - * removed and changed. The services are referred to by type V and - * are made available via the {@link #getServiceInfo} method. + * Cache of registered services. This cache is lazily built by interrogating + * {@link PackageManager} on a per-user basis. It's updated as packages are + * added, removed and changed. Users are responsible for calling + * {@link #invalidateCache(int)} when a user is started, since + * {@link PackageManager} broadcasts aren't sent for stopped users. + * <p> + * The services are referred to by type V and are made available via the + * {@link #getServiceInfo} method. + * * @hide */ public abstract class RegisteredServicesCache<V> { @@ -69,15 +74,29 @@ public abstract class RegisteredServicesCache<V> { private final String mMetaDataName; private final String mAttributesName; private final XmlSerializerAndParser<V> mSerializerAndParser; - private final AtomicReference<BroadcastReceiver> mReceiver; private final Object mServicesLock = new Object(); - // synchronized on mServicesLock - private HashMap<V, Integer> mPersistentServices; - // synchronized on mServicesLock - private Map<V, ServiceInfo<V>> mServices; - // synchronized on mServicesLock + + // @GuardedBy("mServicesLock") private boolean mPersistentServicesFileDidNotExist; + // @GuardedBy("mServicesLock") + private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(); + + private static class UserServices<V> { + // @GuardedBy("mServicesLock") + public final Map<V, Integer> persistentServices = Maps.newHashMap(); + // @GuardedBy("mServicesLock") + public Map<V, ServiceInfo<V>> services = null; + } + + private UserServices<V> findOrCreateUserLocked(int userId) { + UserServices<V> services = mUserServices.get(userId); + if (services == null) { + services = new UserServices<V>(); + mUserServices.put(userId, services); + } + return services; + } /** * This file contains the list of known services. We would like to maintain this forever @@ -102,36 +121,59 @@ public abstract class RegisteredServicesCache<V> { File syncDir = new File(systemDir, "registered_services"); mPersistentServicesFile = new AtomicFile(new File(syncDir, interfaceName + ".xml")); - generateServicesMap(); + // Load persisted services from disk + readPersistentServicesLocked(); - final BroadcastReceiver receiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context1, Intent intent) { - generateServicesMap(); - } - }; - mReceiver = new AtomicReference<BroadcastReceiver>(receiver); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); intentFilter.addDataScheme("package"); - mContext.registerReceiver(receiver, intentFilter); + mContext.registerReceiverAsUser(mPackageReceiver, UserHandle.ALL, intentFilter, null, null); + // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); - mContext.registerReceiver(receiver, sdFilter); + mContext.registerReceiver(mExternalReceiver, sdFilter); } - public void dump(FileDescriptor fd, PrintWriter fout, String[] args) { - Map<V, ServiceInfo<V>> services; + private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); + if (uid != -1) { + generateServicesMap(UserHandle.getUserId(uid)); + } + } + }; + + private final BroadcastReceiver mExternalReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // External apps can't coexist with multi-user, so scan owner + generateServicesMap(UserHandle.USER_OWNER); + } + }; + + public void invalidateCache(int userId) { synchronized (mServicesLock) { - services = mServices; + final UserServices<V> user = findOrCreateUserLocked(userId); + user.services = null; } - fout.println("RegisteredServicesCache: " + services.size() + " services"); - for (ServiceInfo info : services.values()) { - fout.println(" " + info); + } + + public void dump(FileDescriptor fd, PrintWriter fout, String[] args, int userId) { + synchronized (mServicesLock) { + final UserServices<V> user = findOrCreateUserLocked(userId); + if (user.services != null) { + fout.println("RegisteredServicesCache: " + user.services.size() + " services"); + for (ServiceInfo<?> info : user.services.values()) { + fout.println(" " + info); + } + } else { + fout.println("RegisteredServicesCache: services not loaded"); + } } } @@ -151,7 +193,7 @@ public abstract class RegisteredServicesCache<V> { } } - private void notifyListener(final V type, final boolean removed) { + private void notifyListener(final V type, final int userId, final boolean removed) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.d(TAG, "notifyListener: " + type + " is " + (removed ? "removed" : "added")); } @@ -168,7 +210,7 @@ public abstract class RegisteredServicesCache<V> { final RegisteredServicesCacheListener<V> listener2 = listener; handler.post(new Runnable() { public void run() { - listener2.onServiceChanged(type, removed); + listener2.onServiceChanged(type, userId, removed); } }); } @@ -200,9 +242,14 @@ public abstract class RegisteredServicesCache<V> { * @param type the account type of the authenticator * @return the AuthenticatorInfo that matches the account type or null if none is present */ - public ServiceInfo<V> getServiceInfo(V type) { + public ServiceInfo<V> getServiceInfo(V type, int userId) { synchronized (mServicesLock) { - return mServices.get(type); + // Find user and lazily populate cache + final UserServices<V> user = findOrCreateUserLocked(userId); + if (user.services == null) { + generateServicesMap(userId); + } + return user.services.get(type); } } @@ -210,29 +257,15 @@ public abstract class RegisteredServicesCache<V> { * @return a collection of {@link RegisteredServicesCache.ServiceInfo} objects for all * registered authenticators. */ - public Collection<ServiceInfo<V>> getAllServices() { + public Collection<ServiceInfo<V>> getAllServices(int userId) { synchronized (mServicesLock) { - return Collections.unmodifiableCollection(mServices.values()); - } - } - - /** - * Stops the monitoring of package additions, removals and changes. - */ - public void close() { - final BroadcastReceiver receiver = mReceiver.getAndSet(null); - if (receiver != null) { - mContext.unregisterReceiver(receiver); - } - } - - @Override - protected void finalize() throws Throwable { - if (mReceiver.get() != null) { - Log.e(TAG, "RegisteredServicesCache finalized without being closed"); + // Find user and lazily populate cache + final UserServices<V> user = findOrCreateUserLocked(userId); + if (user.services == null) { + generateServicesMap(userId); + } + return Collections.unmodifiableCollection(user.services.values()); } - close(); - super.finalize(); } private boolean inSystemImage(int callerUid) { @@ -251,11 +284,17 @@ public abstract class RegisteredServicesCache<V> { return false; } - public void generateServicesMap() { - PackageManager pm = mContext.getPackageManager(); - ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>(); - List<ResolveInfo> resolveInfos = pm.queryIntentServices(new Intent(mInterfaceName), - PackageManager.GET_META_DATA); + /** + * Populate {@link UserServices#services} by scanning installed packages for + * given {@link UserHandle}. + */ + private void generateServicesMap(int userId) { + Slog.d(TAG, "generateServicesMap() for " + userId); + + final PackageManager pm = mContext.getPackageManager(); + final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>(); + final List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser( + new Intent(mInterfaceName), PackageManager.GET_META_DATA, userId); for (ResolveInfo resolveInfo : resolveInfos) { try { ServiceInfo<V> info = parseServiceInfo(resolveInfo); @@ -272,10 +311,14 @@ public abstract class RegisteredServicesCache<V> { } synchronized (mServicesLock) { - if (mPersistentServices == null) { - readPersistentServicesLocked(); + final UserServices<V> user = findOrCreateUserLocked(userId); + final boolean firstScan = user.services == null; + if (firstScan) { + user.services = Maps.newHashMap(); + } else { + user.services.clear(); } - mServices = Maps.newHashMap(); + StringBuilder changes = new StringBuilder(); for (ServiceInfo<V> info : serviceInfos) { // four cases: @@ -287,19 +330,19 @@ public abstract class RegisteredServicesCache<V> { // - ignore // - exists, the UID is different, and the new one is a system package // - add, notify user that it was added - Integer previousUid = mPersistentServices.get(info.type); + Integer previousUid = user.persistentServices.get(info.type); if (previousUid == null) { changes.append(" New service added: ").append(info).append("\n"); - mServices.put(info.type, info); - mPersistentServices.put(info.type, info.uid); - if (!mPersistentServicesFileDidNotExist) { - notifyListener(info.type, false /* removed */); + user.services.put(info.type, info); + user.persistentServices.put(info.type, info.uid); + if (!(mPersistentServicesFileDidNotExist && firstScan)) { + notifyListener(info.type, userId, false /* removed */); } } else if (previousUid == info.uid) { if (Log.isLoggable(TAG, Log.VERBOSE)) { changes.append(" Existing service (nop): ").append(info).append("\n"); } - mServices.put(info.type, info); + user.services.put(info.type, info); } else if (inSystemImage(info.uid) || !containsTypeAndUid(serviceInfos, info.type, previousUid)) { if (inSystemImage(info.uid)) { @@ -309,9 +352,9 @@ public abstract class RegisteredServicesCache<V> { changes.append(" Existing service replacing a removed service: ") .append(info).append("\n"); } - mServices.put(info.type, info); - mPersistentServices.put(info.type, info.uid); - notifyListener(info.type, false /* removed */); + user.services.put(info.type, info); + user.persistentServices.put(info.type, info.uid); + notifyListener(info.type, userId, false /* removed */); } else { // ignore changes.append(" Existing service with new uid ignored: ").append(info) @@ -320,15 +363,15 @@ public abstract class RegisteredServicesCache<V> { } ArrayList<V> toBeRemoved = Lists.newArrayList(); - for (V v1 : mPersistentServices.keySet()) { + for (V v1 : user.persistentServices.keySet()) { if (!containsType(serviceInfos, v1)) { toBeRemoved.add(v1); } } for (V v1 : toBeRemoved) { - mPersistentServices.remove(v1); + user.persistentServices.remove(v1); changes.append(" Service removed: ").append(v1).append("\n"); - notifyListener(v1, true /* removed */); + notifyListener(v1, userId, true /* removed */); } if (changes.length() > 0) { if (Log.isLoggable(TAG, Log.VERBOSE)) { @@ -342,7 +385,6 @@ public abstract class RegisteredServicesCache<V> { serviceInfos.size() + " services unchanged"); } } - mPersistentServicesFileDidNotExist = false; } } @@ -415,7 +457,7 @@ public abstract class RegisteredServicesCache<V> { * Read all sync status back in to the initial engine state. */ private void readPersistentServicesLocked() { - mPersistentServices = Maps.newHashMap(); + mUserServices.clear(); if (mSerializerAndParser == null) { return; } @@ -444,8 +486,10 @@ public abstract class RegisteredServicesCache<V> { break; } String uidString = parser.getAttributeValue(null, "uid"); - int uid = Integer.parseInt(uidString); - mPersistentServices.put(service, uid); + final int uid = Integer.parseInt(uidString); + final int userId = UserHandle.getUserId(uid); + final UserServices<V> user = findOrCreateUserLocked(userId); + user.persistentServices.put(service, uid); } } eventType = parser.next(); @@ -478,11 +522,14 @@ public abstract class RegisteredServicesCache<V> { out.startDocument(null, true); out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); out.startTag(null, "services"); - for (Map.Entry<V, Integer> service : mPersistentServices.entrySet()) { - out.startTag(null, "service"); - out.attribute(null, "uid", Integer.toString(service.getValue())); - mSerializerAndParser.writeAsXml(service.getKey(), out); - out.endTag(null, "service"); + for (int i = 0; i < mUserServices.size(); i++) { + final UserServices<V> user = mUserServices.valueAt(i); + for (Map.Entry<V, Integer> service : user.persistentServices.entrySet()) { + out.startTag(null, "service"); + out.attribute(null, "uid", Integer.toString(service.getValue())); + mSerializerAndParser.writeAsXml(service.getKey(), out); + out.endTag(null, "service"); + } } out.endTag(null, "services"); out.endDocument(); diff --git a/core/java/android/content/pm/RegisteredServicesCacheListener.java b/core/java/android/content/pm/RegisteredServicesCacheListener.java index 7095229..df79544 100644 --- a/core/java/android/content/pm/RegisteredServicesCacheListener.java +++ b/core/java/android/content/pm/RegisteredServicesCacheListener.java @@ -16,8 +16,6 @@ package android.content.pm; -import android.os.Parcelable; - /** * Listener for changes to the set of registered services managed by a RegisteredServicesCache. * @hide @@ -28,5 +26,5 @@ public interface RegisteredServicesCacheListener<V> { * @param type the type of registered service * @param removed true if the service was removed */ - void onServiceChanged(V type, boolean removed); + void onServiceChanged(V type, int userId, boolean removed); } diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 1e8671b..6624eb8 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -18,13 +18,18 @@ package android.hardware; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.content.Context; import android.graphics.ImageFormat; import android.graphics.Point; import android.graphics.Rect; import android.graphics.SurfaceTexture; +import android.media.IAudioService; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Log; import android.text.TextUtils; import android.view.Surface; @@ -192,7 +197,21 @@ public class Camera { * Returns the information about a particular camera. * If {@link #getNumberOfCameras()} returns N, the valid id is 0 to N-1. */ - public native static void getCameraInfo(int cameraId, CameraInfo cameraInfo); + public static void getCameraInfo(int cameraId, CameraInfo cameraInfo) { + _getCameraInfo(cameraId, cameraInfo); + IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); + IAudioService audioService = IAudioService.Stub.asInterface(b); + try { + if (audioService.isCameraSoundForced()) { + // Only set this when sound is forced; otherwise let native code + // decide. + cameraInfo.canDisableShutterSound = false; + } + } catch (RemoteException e) { + Log.e(TAG, "Audio service is unavailable for queries"); + } + } + private native static void _getCameraInfo(int cameraId, CameraInfo cameraInfo); /** * Information about a camera @@ -1185,7 +1204,20 @@ public class Camera { * @see CameraInfo#canDisableShutterSound * @see ShutterCallback */ - public native final boolean enableShutterSound(boolean enabled); + public final boolean enableShutterSound(boolean enabled) { + if (!enabled) { + IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); + IAudioService audioService = IAudioService.Stub.asInterface(b); + try { + if (audioService.isCameraSoundForced()) return false; + } catch (RemoteException e) { + Log.e(TAG, "Audio service is unavailable for queries"); + } + } + return _enableShutterSound(enabled); + } + + private native final boolean _enableShutterSound(boolean enabled); /** * Callback interface for zoom changes during a smooth zoom operation. diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 83a0c78..2739cac 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -16,6 +16,8 @@ package android.os; import com.android.internal.R; + +import android.app.ActivityManagerNative; import android.content.Context; import android.content.pm.UserInfo; import android.graphics.Bitmap; @@ -82,6 +84,39 @@ public class UserManager { } /** + * Return whether the given user is actively running. This means that + * the user is in the "started" state, not "stopped" -- it is currently + * allowed to run code through scheduled alarms, receiving broadcasts, + * etc. A started user may be either the current foreground user or a + * background user; the result here does not distinguish between the two. + * @param user The user to retrieve the running state for. + */ + public boolean isUserRunning(UserHandle user) { + try { + return ActivityManagerNative.getDefault().isUserRunning( + user.getIdentifier(), false); + } catch (RemoteException e) { + return false; + } + } + + /** + * Return whether the given user is actively running <em>or</em> stopping. + * This is like {@link #isUserRunning(UserHandle)}, but will also return + * true if the user had been running but is in the process of being stopped + * (but is not yet fully stopped, and still running some code). + * @param user The user to retrieve the running state for. + */ + public boolean isUserRunningOrStopping(UserHandle user) { + try { + return ActivityManagerNative.getDefault().isUserRunning( + user.getIdentifier(), true); + } catch (RemoteException e) { + return false; + } + } + + /** * Returns the UserInfo object describing a specific user. * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. * @param userHandle the user handle of the user whose information is being requested. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 3bbdf36..00ea873 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3197,10 +3197,16 @@ public final class Settings { public static final String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern"; /** - * Whether lock pattern will vibrate as user enters (0 = false, 1 = true) + * Whether lock pattern will vibrate as user enters (0 = false, 1 = + * true) + * + * @deprecated Starting in {@link VERSION_CODES#JELLY_BEAN_MR1} the + * lockscreen uses + * {@link Settings.System#HAPTIC_FEEDBACK_ENABLED}. */ - public static final String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = - "lock_pattern_tactile_feedback_enabled"; + @Deprecated + public static final String + LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled"; /** * This preference allows the device to be locked given time after screen goes off, @@ -4052,7 +4058,20 @@ public final class Settings { * @return true if the provider is enabled */ public static final boolean isLocationProviderEnabled(ContentResolver cr, String provider) { - String allowedProviders = Settings.Secure.getString(cr, LOCATION_PROVIDERS_ALLOWED); + return isLocationProviderEnabledForUser(cr, provider, UserHandle.myUserId()); + } + + /** + * Helper method for determining if a location provider is enabled. + * @param cr the content resolver to use + * @param provider the location provider to query + * @param userId the userId to query + * @return true if the provider is enabled + * @hide + */ + public static final boolean isLocationProviderEnabledForUser(ContentResolver cr, String provider, int userId) { + String allowedProviders = Settings.Secure.getStringForUser(cr, + LOCATION_PROVIDERS_ALLOWED, userId); return TextUtils.delimitedStringContains(allowedProviders, ',', provider); } @@ -4064,6 +4083,19 @@ public final class Settings { */ public static final void setLocationProviderEnabled(ContentResolver cr, String provider, boolean enabled) { + setLocationProviderEnabledForUser(cr, provider, enabled, UserHandle.myUserId()); + } + + /** + * Thread-safe method for enabling or disabling a single location provider. + * @param cr the content resolver to use + * @param provider the location provider to enable or disable + * @param enabled true if the provider should be enabled + * @param userId the userId for which to enable/disable providers + * @hide + */ + public static final void setLocationProviderEnabledForUser(ContentResolver cr, + String provider, boolean enabled, int userId) { // to ensure thread safety, we write the provider name with a '+' or '-' // and let the SettingsProvider handle it rather than reading and modifying // the list of enabled providers. @@ -4072,7 +4104,8 @@ public final class Settings { } else { provider = "-" + provider; } - putString(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider); + putStringForUser(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider, + userId); } } @@ -5294,6 +5327,7 @@ public final class Settings { ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED, WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, + WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, WIFI_NUM_OPEN_NETWORKS_KEPT, EMERGENCY_TONE, CALL_AUTO_RETRY, diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index cb78763..f865455 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -120,7 +120,7 @@ public class DreamService extends Service implements Window.Callback { private boolean mInteractive = false; private boolean mLowProfile = true; private boolean mFullscreen = false; - private boolean mScreenBright = false; + private boolean mScreenBright = true; private boolean mFinished; private boolean mDebug = false; diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java index a74e438..ee3f5d8 100644 --- a/core/java/android/view/ScaleGestureDetector.java +++ b/core/java/android/view/ScaleGestureDetector.java @@ -17,6 +17,7 @@ package android.view; import android.content.Context; +import android.content.res.Resources; import android.os.SystemClock; import android.util.FloatMath; @@ -162,9 +163,11 @@ public class ScaleGestureDetector { mContext = context; mListener = listener; mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2; - mTouchMinMajor = - (int) (context.getResources().getDisplayMetrics().density * TOUCH_MIN_MAJOR + 0.5f); - mMinSpan = context.getResources().getDimensionPixelSize( + + final Resources res = context.getResources(); + mTouchMinMajor = res.getDimensionPixelSize( + com.android.internal.R.dimen.config_minScalingTouchMajor); + mMinSpan = res.getDimensionPixelSize( com.android.internal.R.dimen.config_minScalingSpan); } diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 8f4626f..07bb8f9 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -266,6 +266,8 @@ public class Surface implements Parcelable { IBinder displayToken, int orientation, Rect layerStackRect, Rect displayRect); private static native boolean nativeGetDisplayInfo( IBinder displayToken, PhysicalDisplayInfo outInfo); + private static native void nativeBlankDisplay(IBinder displayToken); + private static native void nativeUnblankDisplay(IBinder displayToken); private native void nativeCopyFrom(Surface other); private native void nativeTransferFrom(Surface other); @@ -638,6 +640,22 @@ public class Surface implements Parcelable { return nativeGetDisplayInfo(displayToken, outInfo); } + /** @hide */ + public static void blankDisplay(IBinder displayToken) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + nativeBlankDisplay(displayToken); + } + + /** @hide */ + public static void unblankDisplay(IBinder displayToken) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + nativeUnblankDisplay(displayToken); + } + /** * Copy another surface to this one. This surface now holds a reference * to the same data as the original surface, and is -not- the owner. diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ae51c1d..0d76eac 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6858,12 +6858,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Performs the specified accessibility action on the view. For * possible accessibility actions look at {@link AccessibilityNodeInfo}. - * <p> - * If an {@link AccessibilityDelegate} has been specified via calling - * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its - * {@link AccessibilityDelegate#performAccessibilityAction(View, int, Bundle)} - * is responsible for handling this call. - * </p> + * <p> + * If an {@link AccessibilityDelegate} has been specified via calling + * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its + * {@link AccessibilityDelegate#performAccessibilityAction(View, int, Bundle)} + * is responsible for handling this call. + * </p> * * @param action The action to perform. * @param arguments Optional action arguments. @@ -6886,12 +6886,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, switch (action) { case AccessibilityNodeInfo.ACTION_CLICK: { if (isClickable()) { - return performClick(); + performClick(); + return true; } } break; case AccessibilityNodeInfo.ACTION_LONG_CLICK: { if (isLongClickable()) { - return performLongClick(); + performLongClick(); + return true; } } break; case AccessibilityNodeInfo.ACTION_FOCUS: { diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index b6f0862..2f31ebd 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -648,6 +648,8 @@ public class LinearLayout extends ViewGroup { int largestChildHeight = Integer.MIN_VALUE; + final int layoutDirection = getLayoutDirection(); + // See how tall everyone is. Also remember max width. for (int i = 0; i < count; ++i) { final View child = getVirtualChildAt(i); @@ -667,6 +669,7 @@ public class LinearLayout extends ViewGroup { } LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); + lp.onResolveLayoutDirection(layoutDirection); totalWeight += lp.weight; @@ -989,6 +992,8 @@ public class LinearLayout extends ViewGroup { int largestChildWidth = Integer.MIN_VALUE; + final int layoutDirection = getLayoutDirection(); + // See how wide everyone is. Also remember max height. for (int i = 0; i < count; ++i) { final View child = getVirtualChildAt(i); @@ -1009,6 +1014,7 @@ public class LinearLayout extends ViewGroup { final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); + lp.onResolveLayoutDirection(layoutDirection); totalWeight += lp.weight; |
