Merge "Increase window freeze timeout for bigger screens." into jb-mr1-dev
diff --git a/api/17.txt b/api/17.txt
index f8ad4d9..bebd566 100644
--- a/api/17.txt
+++ b/api/17.txt
@@ -16620,6 +16620,8 @@
     method public android.os.UserHandle getUserForSerialNumber(long);
     method public java.lang.String getUserName();
     method public boolean isUserAGoat();
+    method public boolean isUserRunning(android.os.UserHandle);
+    method public boolean isUserRunningOrStopping(android.os.UserHandle);
   }
 
   public abstract class Vibrator {
diff --git a/api/current.txt b/api/current.txt
index f8ad4d9..bebd566 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -16620,6 +16620,8 @@
     method public android.os.UserHandle getUserForSerialNumber(long);
     method public java.lang.String getUserName();
     method public boolean isUserAGoat();
+    method public boolean isUserRunning(android.os.UserHandle);
+    method public boolean isUserRunningOrStopping(android.os.UserHandle);
   }
 
   public abstract class Vibrator {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 3df88bb..add7a23 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -415,6 +415,10 @@
         ComponentName cn = mAm.startService(null, intent, intent.getType(), mUserId);
         if (cn == null) {
             System.err.println("Error: Not found; no service started.");
+        } else if (cn.getPackageName().equals("!")) {
+            System.err.println("Error: Requires permission " + cn.getClassName());
+        } else if (cn.getPackageName().equals("!!")) {
+            System.err.println("Error: " + cn.getClassName());
         }
     }
 
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 fc569e05..dae38db 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -18,7 +18,7 @@
 
 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,9 @@
 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;
@@ -59,6 +58,8 @@
 
 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,8 +68,8 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
@@ -243,8 +244,7 @@
     }
 
     public void systemReady() {
-        mAuthenticatorCache.generateServicesMap();
-        initUser(0);
+        initUser(UserHandle.USER_OWNER);
     }
 
     private UserManager getUserManager() {
@@ -261,6 +261,7 @@
                 accounts = new UserAccounts(mContext, userId);
                 mUsers.append(userId, accounts);
                 purgeOldGrants(accounts);
+                mAuthenticatorCache.invalidateCache(accounts.userId);
                 validateAccountsAndPopulateCache(accounts);
             }
             return accounts;
@@ -300,6 +301,12 @@
     }
 
     private void validateAccountsAndPopulateCache(UserAccounts accounts) {
+        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,8 +321,8 @@
                     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) {
+
+                    if (!knownAuth.contains(AuthenticatorDescription.newKey(accountType))) {
                         Log.d(TAG, "deleting account " + accountName + " because type "
                                 + accountType + " no longer has a registered authenticator");
                         db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
@@ -390,20 +397,9 @@
         }
     }
 
-    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) {
+        validateAccountsAndPopulateCache(getUserAccounts(userId));
     }
 
     public String getPassword(Account account) {
@@ -470,10 +466,11 @@
                     + "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 +1052,9 @@
         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 +1071,7 @@
         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 +1180,6 @@
                 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 +1481,35 @@
     }
 
     /**
-     * Returns all the accounts qualified by user.
+     * Returns accounts for all running users.
+     *
      * @hide
      */
-    public AccountAndUser[] getAllAccounts() {
-        ArrayList<AccountAndUser> allAccounts = new ArrayList<AccountAndUser>();
-        List<UserInfo> users = getAllUsers();
-        if (users == null)  return new AccountAndUser[0];
+    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);
+        }
 
-        synchronized(mUsers) {
-            for (UserInfo user : users) {
-                UserAccounts userAccounts = getUserAccounts(user.id);
+        final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
+        synchronized (mUsers) {
+            for (int userId : runningUserIds) {
+                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 +1818,9 @@
          * 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
@@ -2083,7 +2065,7 @@
                 }
 
                 fout.println();
-                mAuthenticatorCache.dump(fd, fout, args);
+                mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
             }
         }
     }
@@ -2154,11 +2136,21 @@
         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 +2178,9 @@
     }
 
     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 @@
      * 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 @@
     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 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 @@
         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 @@
         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 c324da92..3e1e358 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1352,10 +1352,16 @@
             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 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 @@
 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 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 @@
                         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 @@
      * {@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 @@
      * {@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 @@
      * {@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 @@
      * @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 @@
 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 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 @@
                         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 @@
      * are ok.
      */
     /*package*/ void close() {
-        if (DBG) log("close()");
+        if (VDBG) log("close()");
 
         IBluetoothManager mgr = mAdapter.getBluetoothManager();
         if (mgr != null) {
@@ -387,7 +388,7 @@
      * {@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 @@
      * {@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 @@
      * {@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 @@
      * @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 @@
      *         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 @@
      * @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 @@
      * @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 @@
      * @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 @@
 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 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 @@
                         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 @@
             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 @@
         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 @@
     }
 
     /*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 @@
 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 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 @@
                         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 @@
     }
 
     /*package*/ void close() {
-        if (DBG) log("close()");
+        if (VDBG) log("close()");
         IBluetoothManager mgr = mAdapter.getBluetoothManager();
         if (mgr != null) {
             try {
@@ -344,7 +345,7 @@
      * {@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 @@
      * {@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 @@
      * {@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 @@
      * @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 @@
     * @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 @@
      * @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 @@
 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 @@
     }
 
     /*package*/ void close() {
-        if (DBG) log("close()");
+        if (VDBG) log("close()");
         if (mConnection != null) {
             mContext.unbindService(mConnection);
             mConnection = null;
@@ -175,7 +176,7 @@
                 }
                 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 @@
      * {@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 @@
      * {@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 @@
      * {@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 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 @@
 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 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 @@
                         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 @@
      *         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 @@
      *         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 @@
      * 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 @@
  */
 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 @@
         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 @@
                     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 @@
         // 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 @@
     }
 
     /*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 @@
                  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 @@
     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 @@
         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 @@
     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 @@
 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 @@
      * 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 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 @@
                         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 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..053cc6f 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -20,8 +20,8 @@
 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.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 @@
 /**
  * @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 @@
 
     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 @@
 
     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 @@
         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 @@
         // 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 @@
         @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 @@
         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 @@
 
         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 @@
                     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 @@
         } 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 @@
             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 @@
                 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,39 @@
         }
     }
 
-    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) {
+        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 removeUser(int userId) {
+    private void onUserStopping(int userId) {
+        updateRunningAccounts();
+
+        cancelActiveSync(
+                null /* any account */,
+                userId,
+                null /* any authority */);
+    }
+
+    private void onUserRemoved(int userId) {
+        updateRunningAccounts();
+
         // Clean up the storage engine database
         mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId);
-        onAccountsUpdated(null);
         synchronized (mSyncQueue) {
             mSyncQueue.removeUser(userId);
         }
@@ -1062,14 +1053,10 @@
     }
 
     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,15 +1072,15 @@
         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 = mRunningAccounts;
 
-        pw.print("accounts: ");
+        pw.print("running accounts: ");
         if (accounts != INITIAL_ACCOUNTS_ARRAY) {
             pw.println(accounts.length);
         } else {
@@ -1153,7 +1140,7 @@
                     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 +1517,23 @@
         }
     }
 
+    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 +1617,10 @@
                 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 +1810,7 @@
                 return earliestFuturePollTime;
             }
 
-            AccountAndUser[] accounts = mAccounts;
+            AccountAndUser[] accounts = mRunningAccounts;
 
             final long nowAbsolute = System.currentTimeMillis();
             final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis)
@@ -1869,9 +1865,10 @@
                         // 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 +1924,7 @@
 
             // If the accounts aren't known yet then we aren't ready to run. We will be kicked
             // when the account lookup request does complete.
-            AccountAndUser[] accounts = mAccounts;
+            AccountAndUser[] accounts = mRunningAccounts;
             if (accounts == INITIAL_ACCOUNTS_ARRAY) {
                 if (isLoggable) {
                     Log.v(TAG, "maybeStartNextSync: accounts not known, skipping");
@@ -1998,7 +1995,7 @@
 
                     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 +2027,7 @@
                 for (Integer user : removedUsers) {
                     // if it's still removed
                     if (mUserManager.getUserInfo(user) == null) {
-                        removeUser(user);
+                        onUserRemoved(user);
                     }
                 }
             }
@@ -2167,8 +2164,8 @@
 
             // 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 c18c86bf..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.os.SystemClock;
-import android.text.format.DateUtils;
-import android.util.Pair;
-import android.util.Log;
 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.Log;
+import android.util.Pair;
+
+import com.google.android.collect.Maps;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -36,7 +37,9 @@
  */
 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 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 @@
     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 @@
         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;
-        synchronized (mServicesLock) {
-            services = mServices;
+    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));
+            }
         }
-        fout.println("RegisteredServicesCache: " + services.size() + " services");
-        for (ServiceInfo info : services.values()) {
-            fout.println("  " + info);
+    };
+
+    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) {
+            final UserServices<V> user = findOrCreateUserLocked(userId);
+            user.services = null;
+        }
+    }
+
+    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 @@
         }
     }
 
-    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 @@
         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 @@
      * @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,31 +257,17 @@
      * @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());
+            // Find user and lazily populate cache
+            final UserServices<V> user = findOrCreateUserLocked(userId);
+            if (user.services == null) {
+                generateServicesMap(userId);
+            }
+            return Collections.unmodifiableCollection(user.services.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");
-        }
-        close();
-        super.finalize();
-    }
-
     private boolean inSystemImage(int callerUid) {
         String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
         for (String name : packages) {
@@ -251,11 +284,17 @@
         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 @@
         }
 
         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 @@
                 //   - 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 @@
                         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 @@
             }
 
             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 @@
                             serviceInfos.size() + " services unchanged");
                 }
             }
-            mPersistentServicesFileDidNotExist = false;
         }
     }
 
@@ -415,7 +457,7 @@
      * 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 @@
                                 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 @@
             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 @@
      * @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 @@
 
 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 @@
      * 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 @@
      * @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 @@
     }
  
     /**
+     * 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 91c6db5..3c4a8fe 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5321,6 +5321,7 @@
             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 cb78763a..f865455 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -120,7 +120,7 @@
     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/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 @@
     /**
      * 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 @@
         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/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index 2811332..4bb6d06 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -476,6 +476,9 @@
                 if (isAction) maxActions--;
 
                 item.setIsActionButton(isAction);
+            } else {
+                // Neither requires nor requests an action button.
+                item.setIsActionButton(false);
             }
         }
         return true;
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 99d49ec..67d831c 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -854,7 +854,7 @@
   { "getNumberOfCameras",
     "()I",
     (void *)android_hardware_Camera_getNumberOfCameras },
-  { "getCameraInfo",
+  { "_getCameraInfo",
     "(ILandroid/hardware/Camera$CameraInfo;)V",
     (void*)android_hardware_Camera_getCameraInfo },
   { "native_setup",
@@ -917,7 +917,7 @@
   { "setDisplayOrientation",
     "(I)V",
     (void *)android_hardware_Camera_setDisplayOrientation },
-  { "enableShutterSound",
+  { "_enableShutterSound",
     "(Z)Z",
     (void *)android_hardware_Camera_enableShutterSound },
   { "_startFaceDetection",
diff --git a/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java b/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java
index 1d7576f..84c9957 100644
--- a/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java
+++ b/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java
@@ -197,7 +197,9 @@
             mServices.add(new ServiceInfo<AuthenticatorDescription>(d2, null, 0));
         }
 
-        public ServiceInfo<AuthenticatorDescription> getServiceInfo(AuthenticatorDescription type) {
+        @Override
+        public ServiceInfo<AuthenticatorDescription> getServiceInfo(
+                AuthenticatorDescription type, int userId) {
             for (ServiceInfo<AuthenticatorDescription> service : mServices) {
                 if (service.type.equals(type)) {
                     return service;
@@ -206,21 +208,25 @@
             return null;
         }
 
-        public Collection<ServiceInfo<AuthenticatorDescription>> getAllServices() {
+        @Override
+        public Collection<ServiceInfo<AuthenticatorDescription>> getAllServices(int userId) {
             return mServices;
         }
 
-        public void dump(final FileDescriptor fd, final PrintWriter fout, final String[] args) {
+        @Override
+        public void dump(
+                final FileDescriptor fd, final PrintWriter fout, final String[] args, int userId) {
         }
 
+        @Override
         public void setListener(
                 final RegisteredServicesCacheListener<AuthenticatorDescription> listener,
                 final Handler handler) {
         }
 
-        @Override
-        public void generateServicesMap() {
-        }
+		@Override
+		public void invalidateCache(int userId) {
+		}
     }
 
     static public class MyMockContext extends MockContext {
diff --git a/docs/html/distribute/googleplay/quality/core.jd b/docs/html/distribute/googleplay/quality/core.jd
index 291550f..c1ef68c 100644
--- a/docs/html/distribute/googleplay/quality/core.jd
+++ b/docs/html/distribute/googleplay/quality/core.jd
@@ -589,8 +589,9 @@
 one or two devices per form factor. </p>
 
 <p>If you are not able to obtain actual hardware devices for testing, you should
-set up emulated devices (AVDs) to represent the most common form factors and
-hardware/software combinations. </p>
+<a href="{@docRoot}tools/devices/index.html">set up emulated devices (AVDs)</a>
+to represent the most common form factors and
+hardware/software combinations.</p>
 
 <p>To go beyond basic testing, you can add more devices, more form factors, or
 new hardware/software combinations to your test environment. You can also
diff --git a/docs/html/distribute/googleplay/quality/tablet.jd b/docs/html/distribute/googleplay/quality/tablet.jd
index f180f54..80346a7 100644
--- a/docs/html/distribute/googleplay/quality/tablet.jd
+++ b/docs/html/distribute/googleplay/quality/tablet.jd
@@ -528,7 +528,8 @@
 devices you could use for testing.</p>
 
 <p>If you are not able to obtain actual hardware devices for testing, you should
-set up emulated devices (AVDs) to represent the most common form factors and
+<a href="{@docRoot}tools/devices/index.html">set up emulated devices (AVDs)</a>
+to represent the most common form factors and
 hardware/software combinations. See the table below for suggestions on the emulator
 configurations to use. </p>
 
diff --git a/docs/html/distribute/googleplay/spotlight/tablets.jd b/docs/html/distribute/googleplay/spotlight/tablets.jd
index f968a40..ee256bc 100644
--- a/docs/html/distribute/googleplay/spotlight/tablets.jd
+++ b/docs/html/distribute/googleplay/spotlight/tablets.jd
@@ -152,12 +152,13 @@
   </div>
 
   <div style="line-height:1.4em;">
-    <p style="margin-top:0;margin-bottom:12px;">Over a year ago, developer
-TinyCo, makers of games such as Tiny Monsters, switched to a
-simultaneous launch strategy for their products. They chose Android as one of their
-primary launch platforms because of its large installed base and global reach. They
-also knew that the growing base of Android tablet users represented a huge
-opportunity. </p>
+    <p style="margin-top:0;margin-bottom:12px;">
+    
+<p>Over a year ago, app developer TinyCo, makers of a suite of games such as
+Tiny Monsters, decided to prioritize launching across multiple platforms
+effectively. They chose Android as one of their primary launch platforms because
+of its large installed base and global reach. They also knew that the growing
+base of Android tablet users represented a huge opportunity.</p>
     
     <p>Tiny Village was their first title to take advantage of the strategy, and
 it proved to be a winning one &mdash; especially in terms of Android
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 6ba57809..4604437 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -413,6 +413,11 @@
         }
 
         nativeCopyPixelsFromBuffer(mNativeBitmap, src);
+
+        // now update the buffer's position
+        int position = src.position();
+        position += bitmapBytes >> shift;
+        src.position(position);
     }
 
     /**
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPerfTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPerfTestRunner.java
index a6cf355..3d5905d 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPerfTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPerfTestRunner.java
@@ -38,7 +38,7 @@
 public class MediaFrameworkPerfTestRunner extends InstrumentationTestRunner {
 
     public static boolean mGetNativeHeapDump = false;
-
+    public static boolean mGetProcmem = false;
 
     @Override
     public TestSuite getAllTests() {
@@ -61,6 +61,12 @@
         if (get_heap_dump != null) {
             mGetNativeHeapDump = true;
         }
+
+        String get_procmem = (String) icicle.get("get_procmem");
+        if (get_procmem != null) {
+            mGetProcmem = true;
+        }
+
     }
 }
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
index ccb0638..9b1098e 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -1,12 +1,12 @@
 /*
  * Copyright (C) 2008 The Android Open Source Project
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
  * the License at
- * 
+ *
  * http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -53,7 +53,7 @@
 import com.android.mediaframeworktest.MediaProfileReader;
 
 /**
- * Junit / Instrumentation - performance measurement for media player and 
+ * Junit / Instrumentation - performance measurement for media player and
  * recorder
  *
  * FIXME:
@@ -100,6 +100,7 @@
         super("com.android.mediaframeworktest", MediaFrameworkTest.class);
     }
 
+    @Override
     protected void setUp() throws Exception {
         super.setUp();
         //Insert a 2 second before launching the test activity. This is
@@ -109,19 +110,26 @@
         if (MediaFrameworkPerfTestRunner.mGetNativeHeapDump)
             MediaTestUtil.getNativeHeapDump(this.getName() + "_before");
 
-        mProcMemWriter = new BufferedWriter(new FileWriter
-                (new File(MEDIA_PROCMEM_OUTPUT), true));
-        mProcMemWriter.write(this.getName() + "\n");
-        mMemWriter = new BufferedWriter(new FileWriter
-                (new File(MEDIA_MEMORY_OUTPUT), true));
+        if (MediaFrameworkPerfTestRunner.mGetProcmem) {
+            mProcMemWriter = new BufferedWriter(new FileWriter
+                    (new File(MEDIA_PROCMEM_OUTPUT), true));
+            mProcMemWriter.write(this.getName() + "\n");
+            mMemWriter = new BufferedWriter(new FileWriter
+                    (new File(MEDIA_MEMORY_OUTPUT), true));
+        }
 
     }
 
+    @Override
     protected void tearDown() throws Exception {
         if (MediaFrameworkPerfTestRunner.mGetNativeHeapDump)
             MediaTestUtil.getNativeHeapDump(this.getName() + "_after");
-        mProcMemWriter.close();
-        mMemWriter.close();
+
+        if (MediaFrameworkPerfTestRunner.mGetProcmem) {
+            mMemWriter.write("\n");
+            mProcMemWriter.close();
+            mMemWriter.close();
+        }
         super.tearDown();
     }
 
@@ -157,6 +165,7 @@
     }
 
     private final class RawPreviewCallback implements PreviewCallback {
+        @Override
         public void onPreviewFrame(byte[] rawData, Camera camera) {
             mPreviewDone.open();
         }
@@ -285,19 +294,21 @@
         }
     }
 
-    public void writeProcmemInfo() throws Exception{
-        String cmd = "procmem " + getMediaserverPid();
-        Process p = Runtime.getRuntime().exec(cmd);
+    public void writeProcmemInfo() throws Exception {
+        if (MediaFrameworkPerfTestRunner.mGetProcmem) {
+            String cmd = "procmem " + getMediaserverPid();
+            Process p = Runtime.getRuntime().exec(cmd);
 
-        InputStream inStream = p.getInputStream();
-        InputStreamReader inReader = new InputStreamReader(inStream);
-        BufferedReader inBuffer = new BufferedReader(inReader);
-        String s;
-        while ((s = inBuffer.readLine()) != null) {
-              mProcMemWriter.write(s);
-              mProcMemWriter.write("\n");
+            InputStream inStream = p.getInputStream();
+            InputStreamReader inReader = new InputStreamReader(inStream);
+            BufferedReader inBuffer = new BufferedReader(inReader);
+            String s;
+            while ((s = inBuffer.readLine()) != null) {
+                mProcMemWriter.write(s);
+                mProcMemWriter.write("\n");
+            }
+            mProcMemWriter.write("\n\n");
         }
-        mProcMemWriter.write("\n\n");
     }
 
     public String captureMediaserverInfo() {
@@ -368,13 +379,11 @@
         boolean memoryResult = false;
 
         mStartPid = getMediaserverPid();
-        mMemWriter.write("H263 Video Playback Only\n");
         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
             mediaStressPlayback(MediaNames.VIDEO_HIGHRES_H263);
             getMemoryWriteToLog(i);
             writeProcmemInfo();
         }
-        mMemWriter.write("\n");
         memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT);
         assertTrue("H263 playback memory test", memoryResult);
     }
@@ -385,13 +394,11 @@
         boolean memoryResult = false;
 
         mStartPid = getMediaserverPid();
-        mMemWriter.write("H264 Video Playback only\n");
         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
             mediaStressPlayback(MediaNames.VIDEO_H264_AMR);
             getMemoryWriteToLog(i);
             writeProcmemInfo();
         }
-        mMemWriter.write("\n");
         memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT);
         assertTrue("H264 playback memory test", memoryResult);
     }
@@ -402,7 +409,6 @@
         boolean memoryResult = false;
 
         mStartPid = getMediaserverPid();
-        mMemWriter.write("H263 video record only\n");
         int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H263);
         assertTrue("H263 video recording frame rate", frameRate != -1);
         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
@@ -411,7 +417,6 @@
             getMemoryWriteToLog(i);
             writeProcmemInfo();
         }
-        mMemWriter.write("\n");
         memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
         assertTrue("H263 record only memory test", memoryResult);
     }
@@ -422,7 +427,6 @@
         boolean memoryResult = false;
 
         mStartPid = getMediaserverPid();
-        mMemWriter.write("MPEG4 video record only\n");
         int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.MPEG_4_SP);
         assertTrue("MPEG4 video recording frame rate", frameRate != -1);
         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
@@ -431,7 +435,6 @@
             getMemoryWriteToLog(i);
             writeProcmemInfo();
         }
-        mMemWriter.write("\n");
         memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
         assertTrue("mpeg4 record only memory test", memoryResult);
     }
@@ -445,14 +448,12 @@
         mStartPid = getMediaserverPid();
         int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H263);
         assertTrue("H263 video recording frame rate", frameRate != -1);
-        mMemWriter.write("Audio and h263 video record\n");
         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
             assertTrue(stressVideoRecord(frameRate, 352, 288, MediaRecorder.VideoEncoder.H263,
                     MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, false));
             getMemoryWriteToLog(i);
             writeProcmemInfo();
         }
-        mMemWriter.write("\n");
         memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
         assertTrue("H263 audio video record memory test", memoryResult);
     }
@@ -463,13 +464,11 @@
         boolean memoryResult = false;
 
         mStartPid = getMediaserverPid();
-        mMemWriter.write("Audio record only\n");
         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
             stressAudioRecord(MediaNames.RECORDER_OUTPUT);
             getMemoryWriteToLog(i);
             writeProcmemInfo();
         }
-        mMemWriter.write("\n");
         memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
         assertTrue("audio record only memory test", memoryResult);
     }
@@ -480,13 +479,11 @@
         boolean memoryResult = false;
 
         mStartPid = getMediaserverPid();
-        mMemWriter.write("Camera Preview Only\n");
         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
             stressCameraPreview();
             getMemoryWriteToLog(i);
             writeProcmemInfo();
         }
-        mMemWriter.write("\n");
         memoryResult = validateMemoryResult(mStartPid, mStartMemory, CAMERA_LIMIT);
         assertTrue("camera preview memory test", memoryResult);
     }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 0a0474c..0b85e70 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -26,6 +26,7 @@
 import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.os.FileUtils;
+import android.os.Handler;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.provider.Settings;
@@ -61,12 +62,6 @@
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_BACKUP = DEBUG || false;
 
-    /* Don't restore wifi config until we have new logic for parsing the
-     * saved wifi config and configuring the new APs without having to
-     * disable and re-enable wifi
-     */
-    private static final boolean NAIVE_WIFI_RESTORE = false;
-
     private static final String KEY_SYSTEM = "system";
     private static final String KEY_SECURE = "secure";
     private static final String KEY_GLOBAL = "global";
@@ -127,10 +122,16 @@
     // stored in the full-backup tarfile as well, so should not be changed.
     private static final String STAGE_FILE = "flattened-data";
 
+    // Delay in milliseconds between the restore operation and when we will bounce
+    // wifi in order to rewrite the supplicant config etc.
+    private static final long WIFI_BOUNCE_DELAY_MILLIS = 60 * 1000; // one minute
+
     private SettingsHelper mSettingsHelper;
     private WifiManager mWfm;
     private static String mWifiConfigFile;
 
+    WifiRestoreRunnable mWifiRestore = null;
+
     // Class for capturing a network definition from the wifi supplicant config file
     static class Network {
         String ssid = "";  // equals() and hashCode() need these to be non-null
@@ -297,6 +298,66 @@
         writeNewChecksums(stateChecksums, newState);
     }
 
+    class WifiRestoreRunnable implements Runnable {
+        private byte[] restoredSupplicantData;
+        private byte[] restoredWifiConfigFile;
+
+        void incorporateWifiSupplicant(BackupDataInput data) {
+            restoredSupplicantData = new byte[data.getDataSize()];
+            if (restoredSupplicantData.length <= 0) return;
+            try {
+                data.readEntityData(restoredSupplicantData, 0, data.getDataSize());
+            } catch (IOException e) {
+                Log.w(TAG, "Unable to read supplicant data");
+                restoredSupplicantData = null;
+            }
+        }
+
+        void incorporateWifiConfigFile(BackupDataInput data) {
+            restoredWifiConfigFile = new byte[data.getDataSize()];
+            if (restoredWifiConfigFile.length <= 0) return;
+            try {
+                data.readEntityData(restoredWifiConfigFile, 0, data.getDataSize());
+            } catch (IOException e) {
+                Log.w(TAG, "Unable to read config file");
+                restoredWifiConfigFile = null;
+            }
+        }
+
+        @Override
+        public void run() {
+            if (restoredSupplicantData != null || restoredWifiConfigFile != null) {
+                if (DEBUG_BACKUP) {
+                    Log.v(TAG, "Starting deferred restore of wifi data");
+                }
+                final int retainedWifiState = enableWifi(false);
+                if (restoredSupplicantData != null) {
+                    restoreWifiSupplicant(FILE_WIFI_SUPPLICANT,
+                            restoredSupplicantData, restoredSupplicantData.length);
+                    FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
+                            FileUtils.S_IRUSR | FileUtils.S_IWUSR |
+                            FileUtils.S_IRGRP | FileUtils.S_IWGRP,
+                            Process.myUid(), Process.WIFI_UID);
+                }
+                if (restoredWifiConfigFile != null) {
+                    restoreFileData(mWifiConfigFile,
+                            restoredWifiConfigFile, restoredWifiConfigFile.length);
+                }
+                // restore the previous WIFI state.
+                enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED ||
+                        retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
+            }
+        }
+    }
+
+    // Instantiate the wifi-config restore runnable, scheduling it for execution
+    // a minute hence
+    void initWifiRestoreIfNecessary() {
+        if (mWifiRestore == null) {
+            mWifiRestore = new WifiRestoreRunnable();
+        }
+    }
+
     @Override
     public void onRestore(BackupDataInput data, int appVersionCode,
             ParcelFileDescriptor newState) throws IOException {
@@ -315,26 +376,26 @@
                 restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal);
             } else if (KEY_GLOBAL.equals(key)) {
                 restoreSettings(data, Settings.Global.CONTENT_URI, null);
-            } else if (NAIVE_WIFI_RESTORE && KEY_WIFI_SUPPLICANT.equals(key)) {
-                int retainedWifiState = enableWifi(false);
-                restoreWifiSupplicant(FILE_WIFI_SUPPLICANT, data);
-                FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
-                        FileUtils.S_IRUSR | FileUtils.S_IWUSR |
-                        FileUtils.S_IRGRP | FileUtils.S_IWGRP,
-                        Process.myUid(), Process.WIFI_UID);
-                // retain the previous WIFI state.
-                enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED ||
-                        retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
+            } else if (KEY_WIFI_SUPPLICANT.equals(key)) {
+                initWifiRestoreIfNecessary();
+                mWifiRestore.incorporateWifiSupplicant(data);
             } else if (KEY_LOCALE.equals(key)) {
                 byte[] localeData = new byte[size];
                 data.readEntityData(localeData, 0, size);
                 mSettingsHelper.setLocaleData(localeData, size);
-            } else if (NAIVE_WIFI_RESTORE && KEY_WIFI_CONFIG.equals(key)) {
-                restoreFileData(mWifiConfigFile, data);
+            } else if (KEY_WIFI_CONFIG.equals(key)) {
+                initWifiRestoreIfNecessary();
+                mWifiRestore.incorporateWifiConfigFile(data);
              } else {
                 data.skipEntityData();
             }
         }
+
+        // If we have wifi data to restore, post a runnable to perform the
+        // bounce-and-update operation a little ways in the future.
+        if (mWifiRestore != null) {
+            new Handler(getMainLooper()).postDelayed(mWifiRestore, WIFI_BOUNCE_DELAY_MILLIS);
+        }
     }
 
     @Override
@@ -619,7 +680,7 @@
                 getContentResolver().insert(destination, contentValues);
             }
 
-            if (DEBUG || true) {
+            if (DEBUG) {
                 Log.d(TAG, "Restored setting: " + destination + " : "+ key + "=" + value);
             }
         }
@@ -731,17 +792,6 @@
 
     }
 
-    private void restoreFileData(String filename, BackupDataInput data) {
-        byte[] bytes = new byte[data.getDataSize()];
-        if (bytes.length <= 0) return;
-        try {
-            data.readEntityData(bytes, 0, data.getDataSize());
-            restoreFileData(filename, bytes, bytes.length);
-        } catch (IOException e) {
-            Log.w(TAG, "Unable to read file data for " + filename);
-        }
-    }
-
     private void restoreFileData(String filename, byte[] bytes, int size) {
         try {
             File file = new File(filename);
@@ -794,17 +844,6 @@
         }
     }
 
-    private void restoreWifiSupplicant(String filename, BackupDataInput data) {
-        byte[] bytes = new byte[data.getDataSize()];
-        if (bytes.length <= 0) return;
-        try {
-            data.readEntityData(bytes, 0, data.getDataSize());
-            restoreWifiSupplicant(filename, bytes, bytes.length);
-        } catch (IOException e) {
-            Log.w(TAG, "Unable to read supplicant data");
-        }
-    }
-
     private void restoreWifiSupplicant(String filename, byte[] bytes, int size) {
         try {
             WifiNetworkSettings supplicantImage = new WifiNetworkSettings();
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 64b660f..f0e5a87 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -108,6 +108,7 @@
         </activity>
 
         <activity android:name=".recent.RecentsActivity"
+                android:label="@string/accessibility_desc_recent_apps"
                 android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar"
                 android:excludeFromRecents="true"
                 android:launchMode="singleInstance"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7ac27fe..e0b0227 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -377,6 +377,8 @@
     <string name="accessibility_desc_notification_shade">Notification shade.</string>
     <!-- Content description for the quick settings panel (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_desc_quick_settings">Quick settings.</string>
+    <!-- Content description for the recent apps panel (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_desc_recent_apps">Recent apps.</string>
 
     <!-- Content description of the user tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_quick_settings_user">User <xliff:g id="user" example="John Doe">%s</xliff:g>.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index e8772df..0937c46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -102,7 +102,7 @@
     private int mBrightnessDialogShortTimeout;
     private int mBrightnessDialogLongTimeout;
 
-    private AsyncTask<Void, Void, Pair<String, BitmapDrawable>> mUserInfoTask;
+    private AsyncTask<Void, Void, Pair<String, Drawable>> mUserInfoTask;
 
     private LevelListDrawable mBatteryLevels;
     private LevelListDrawable mChargingBatteryLevels;
@@ -203,35 +203,43 @@
         final int userId = userInfo.id;
 
         final Context context = currentUserContext;
-        mUserInfoTask = new AsyncTask<Void, Void, Pair<String, BitmapDrawable>>() {
+        mUserInfoTask = new AsyncTask<Void, Void, Pair<String, Drawable>>() {
             @Override
-            protected Pair<String, BitmapDrawable> doInBackground(Void... params) {
-                Cursor cursor = context.getContentResolver().query(
+            protected Pair<String, Drawable> doInBackground(Void... params) {
+                final Cursor cursor = context.getContentResolver().query(
                         Profile.CONTENT_URI, new String[] {Phone._ID, Phone.DISPLAY_NAME},
                         null, null, null);
+                final UserManager um =
+                        (UserManager) mContext.getSystemService(Context.USER_SERVICE);
 
-                if (cursor == null) {
-                    // Info not available. Should become available later.
-                    return new Pair<String, BitmapDrawable>(null, null);
+                // Fall back to the UserManager nickname if we can't read the name from the local
+                // profile below.
+                String nickName = um.getUserName();
+                String name = nickName;
+                Drawable avatar = null;
+                Bitmap rawAvatar = um.getUserIcon(userId);
+                if (rawAvatar != null) {
+                    avatar = new BitmapDrawable(mContext.getResources(), rawAvatar);
+                } else {
+                    avatar = mContext.getResources().getDrawable(R.drawable.ic_qs_default_user);
                 }
 
-                String name = null;
-                try {
-                    if (cursor.moveToFirst()) {
-                        name = cursor.getString(cursor.getColumnIndex(Phone.DISPLAY_NAME));
+                // Try and read the display name from the local profile
+                if (cursor != null) {
+                    try {
+                        if (cursor.moveToFirst()) {
+                            name = cursor.getString(cursor.getColumnIndex(Phone.DISPLAY_NAME));
+                        }
+                    } finally {
+                        cursor.close();
                     }
-                } finally {
-                    cursor.close();
                 }
-                final UserManager userManager =
-                    (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-                final BitmapDrawable icon = new BitmapDrawable(mContext.getResources(),
-                        userManager.getUserIcon(userId));
-                return new Pair<String, BitmapDrawable>(name, icon);
+
+                return new Pair<String, Drawable>(name, avatar);
             }
 
             @Override
-            protected void onPostExecute(Pair<String, BitmapDrawable> result) {
+            protected void onPostExecute(Pair<String, Drawable> result) {
                 super.onPostExecute(result);
                 mModel.setUserTileInfo(result.first, result.second);
                 mUserInfoTask = null;
@@ -305,9 +313,7 @@
                 ImageView iv = (ImageView) view.findViewById(R.id.user_imageview);
                 TextView tv = (TextView) view.findViewById(R.id.user_textview);
                 tv.setText(state.label);
-                if (us.avatar != null) {
-                    iv.setImageDrawable(us.avatar);
-                }
+                iv.setImageDrawable(us.avatar);
                 view.setContentDescription(mContext.getString(
                         R.string.accessibility_quick_settings_user, state.label));
             }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index 6ea3513..840edaf 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -33,6 +33,8 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.os.Looper;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.UserManager;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -43,12 +45,14 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.view.View.BaseSavedState;
 import android.view.animation.AnimationUtils;
 import android.widget.RemoteViews.OnClickHandler;
 import android.widget.ViewFlipper;
 
 import com.android.internal.R;
 import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.internal.policy.impl.keyguard.KeyguardTransportControlView.SavedState;
 import com.android.internal.widget.LockPatternUtils;
 
 import java.io.File;
@@ -64,6 +68,10 @@
     static final int APPWIDGET_HOST_ID = 0x4B455947;
     private static final String KEYGUARD_WIDGET_PREFS = "keyguard_widget_prefs";
 
+    private static final int TRANSPORT_GONE = 0;
+    private static final int TRANSPORT_INVISIBLE = 1;
+    private static final int TRANSPORT_VISIBLE = 2;
+
     private AppWidgetHost mAppWidgetHost;
     private KeyguardWidgetRegion mAppWidgetRegion;
     private KeyguardWidgetPager mAppWidgetContainer;
@@ -83,10 +91,12 @@
     private KeyguardSecurityModel mSecurityModel;
 
     private Rect mTempRect = new Rect();
+    private int mTransportState = TRANSPORT_GONE;
 
     /*package*/ interface TransportCallback {
-        void hide();
-        void show();
+        void onListenerDetached();
+        void onListenerAttached();
+        void onPlayStateChanged();
     }
 
     /*package*/ interface UserSwitcherCallback {
@@ -185,7 +195,7 @@
         mAppWidgetHost.startListening();
         maybePopulateWidgets();
         disableStatusViewInteraction();
-        showAppropriateWidgetPage();
+        post(mSwitchPageRunnable);
     }
 
     private void disableStatusViewInteraction() {
@@ -712,7 +722,7 @@
     private void addDefaultWidgets() {
         LayoutInflater inflater = LayoutInflater.from(mContext);
         inflater.inflate(R.layout.keyguard_status_view, mAppWidgetContainer, true);
-        inflater.inflate(R.layout.keyguard_transport_control_view, mAppWidgetContainer, true);
+        inflater.inflate(R.layout.keyguard_transport_control_view, this, true);
 
         inflateAndAddUserSelectorWidgetIfNecessary();
         initializeTransportControl();
@@ -721,16 +731,16 @@
     private void initializeTransportControl() {
         mTransportControl =
             (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control);
+        mTransportControl.setVisibility(View.GONE);
 
         // This code manages showing/hiding the transport control. We keep it around and only
         // add it to the hierarchy if it needs to be present.
         if (mTransportControl != null) {
             mTransportControl.setKeyguardCallback(new TransportCallback() {
-                boolean mSticky = false;
                 @Override
-                public void hide() {
+                public void onListenerDetached() {
                     int page = getWidgetPosition(R.id.keyguard_transport_control);
-                    if (page != -1 && !mSticky) {
+                    if (page != -1) {
                         if (page == mAppWidgetContainer.getCurrentPage()) {
                             // Switch back to clock view if music was showing.
                             mAppWidgetContainer
@@ -741,20 +751,23 @@
                         // from AudioManager
                         KeyguardHostView.this.addView(mTransportControl);
                         mTransportControl.setVisibility(View.GONE);
+                        mTransportState = TRANSPORT_GONE;
                     }
                 }
 
                 @Override
-                public void show() {
+                public void onListenerAttached() {
                     if (getWidgetPosition(R.id.keyguard_transport_control) == -1) {
                         KeyguardHostView.this.removeView(mTransportControl);
-                        mAppWidgetContainer.addView(mTransportControl,
-                                getWidgetPosition(R.id.keyguard_status_view) + 1);
+                        mAppWidgetContainer.addView(mTransportControl, 0);
                         mTransportControl.setVisibility(View.VISIBLE);
-                        // Once shown, leave it showing
-                        mSticky = true;
                     }
                 }
+
+                @Override
+                public void onPlayStateChanged() {
+                    mTransportControl.post(mSwitchPageRunnable);
+                }
             });
         }
     }
@@ -796,12 +809,87 @@
         }
     }
 
-    private void showAppropriateWidgetPage() {
-        int page = mAppWidgetContainer.indexOfChild(findViewById(R.id.keyguard_status_view));
-        if (mAppWidgetContainer.indexOfChild(mTransportControl) != -1) {
-            page = mAppWidgetContainer.indexOfChild(mTransportControl);
+    Runnable mSwitchPageRunnable = new Runnable() {
+        @Override
+        public void run() {
+           showAppropriateWidgetPage();
         }
-        mAppWidgetContainer.setCurrentPage(page);
+    };
+
+    static class SavedState extends BaseSavedState {
+        int transportState;
+
+        SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        private SavedState(Parcel in) {
+            super(in);
+            this.transportState = in.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            super.writeToParcel(out, flags);
+            out.writeInt(this.transportState);
+        }
+
+        public static final Parcelable.Creator<SavedState> CREATOR
+                = new Parcelable.Creator<SavedState>() {
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+
+    @Override
+    public Parcelable onSaveInstanceState() {
+        Parcelable superState = super.onSaveInstanceState();
+        SavedState ss = new SavedState(superState);
+        ss.transportState = mTransportState;
+        return ss;
+    }
+
+    @Override
+    public void onRestoreInstanceState(Parcelable state) {
+        if (!(state instanceof SavedState)) {
+            super.onRestoreInstanceState(state);
+            return;
+        }
+        SavedState ss = (SavedState) state;
+        super.onRestoreInstanceState(ss.getSuperState());
+        mTransportState = ss.transportState;
+        post(mSwitchPageRunnable);
+    }
+
+    private void showAppropriateWidgetPage() {
+
+        // The following sets the priority for showing widgets. Transport should be shown if
+        // music is playing, followed by the multi-user widget if enabled, followed by the
+        // status widget.
+        final int pageToShow;
+        if (mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE) {
+            mTransportState = TRANSPORT_VISIBLE;
+            pageToShow = mAppWidgetContainer.indexOfChild(mTransportControl);
+        } else {
+            UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+            final View multiUserView = findViewById(R.id.keyguard_multi_user_selector);
+            final int multiUserPosition = mAppWidgetContainer.indexOfChild(multiUserView);
+            if (multiUserPosition != -1 && mUm.getUsers(true).size() > 1) {
+                pageToShow = multiUserPosition;
+            } else {
+                final View statusView = findViewById(R.id.keyguard_status_view);
+                pageToShow = mAppWidgetContainer.indexOfChild(statusView);
+            }
+            if (mTransportState == TRANSPORT_VISIBLE) {
+                mTransportState = TRANSPORT_INVISIBLE;
+            }
+        }
+        mAppWidgetContainer.setCurrentPage(pageToShow);
     }
 
     private void inflateAndAddUserSelectorWidgetIfNecessary() {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
index e3b7b01..b6b731e 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
@@ -28,6 +28,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.MediaStore;
+import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Slog;
@@ -203,11 +204,18 @@
         } else if (disabledBySimState) {
             Log.v(TAG, "Camera disabled by Sim State");
         }
+        boolean currentUserSetup = 0 != Settings.Secure.getIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.USER_SETUP_COMPLETE,
+                0 /*default */,
+                currentUserHandle);
         boolean searchActionAvailable =
                 ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
                 .getAssistIntent(mContext, UserHandle.USER_CURRENT) != null;
-        mCameraDisabled = cameraDisabledByAdmin || disabledBySimState || !cameraTargetPresent;
-        mSearchDisabled = disabledBySimState || !searchActionAvailable || !searchTargetPresent;
+        mCameraDisabled = cameraDisabledByAdmin || disabledBySimState || !cameraTargetPresent
+                || !currentUserSetup;
+        mSearchDisabled = disabledBySimState || !searchActionAvailable || !searchTargetPresent
+                || !currentUserSetup;
         updateResources();
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
index e2f3059..7e71f94 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
@@ -42,7 +42,6 @@
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.View.OnClickListener;
-import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -59,7 +58,7 @@
     private static final int MSG_SET_GENERATION_ID = 104;
     private static final int MAXDIM = 512;
     private static final int DISPLAY_TIMEOUT_MS = 5000; // 5s
-    protected static final boolean DEBUG = false;
+    protected static final boolean DEBUG = true;
     protected static final String TAG = "TransportControlView";
 
     private ImageView mAlbumArt;
@@ -75,6 +74,7 @@
     private int mCurrentPlayState;
     private AudioManager mAudioManager;
     private IRemoteControlDisplayWeak mIRCD;
+    private boolean mMusicClientPresent = true;
 
     /**
      * The metadata which should be populated into the view once we've been attached
@@ -112,7 +112,9 @@
             case MSG_SET_GENERATION_ID:
                 if (msg.arg2 != 0) {
                     // This means nobody is currently registered. Hide the view.
-                    hide();
+                    onListenerDetached();
+                } else {
+                    onListenerAttached();
                 }
                 if (DEBUG) Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + msg.arg2);
                 mClientGeneration = msg.arg1;
@@ -193,28 +195,26 @@
         mIRCD = new IRemoteControlDisplayWeak(mHandler);
     }
 
-    protected void hide() {
-        if (DEBUG) Log.v(TAG, "Transport was told to hide");
+    protected void onListenerDetached() {
+        mMusicClientPresent = false;
+        if (DEBUG) Log.v(TAG, "onListenerDetached()");
         if (mTransportCallback != null) {
-            mTransportCallback.hide();
+            mTransportCallback.onListenerDetached();
         } else {
-            Log.w(TAG, "Hide music, but callback wasn't set");
+            Log.w(TAG, "onListenerDetached: no callback");
         }
     }
 
-    private void show() {
-        if (DEBUG) Log.v(TAG, "Transport was told to show");
+    private void onListenerAttached() {
+        mMusicClientPresent = true;
+        if (DEBUG) Log.v(TAG, "onListenerAttached()");
         if (mTransportCallback != null) {
-            mTransportCallback.show();
+            mTransportCallback.onListenerAttached();
         } else {
-            Log.w(TAG, "Show music, but callback wasn't set");
+            Log.w(TAG, "onListenerAttached(): no callback");
         }
     }
 
-    private void userActivity() {
-        // TODO Auto-generated method stub
-    }
-
     private void updateTransportControls(int transportControlFlags) {
         mTransportControlFlags = transportControlFlags;
     }
@@ -341,6 +341,11 @@
         updatePlayPauseState(mCurrentPlayState);
     }
 
+    public boolean isMusicPlaying() {
+       return mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING
+               || mCurrentPlayState == RemoteControlClient.PLAYSTATE_BUFFERING;
+    }
+
     private static void setVisibilityBasedOnFlag(View view, int flags, int flag) {
         if ((flags & flag) != 0) {
             view.setVisibility(View.VISIBLE);
@@ -357,7 +362,6 @@
         }
         final int imageResId;
         final int imageDescId;
-        boolean showIfHidden = false;
         switch (state) {
             case RemoteControlClient.PLAYSTATE_ERROR:
                 imageResId = com.android.internal.R.drawable.stat_sys_warning;
@@ -369,32 +373,27 @@
             case RemoteControlClient.PLAYSTATE_PLAYING:
                 imageResId = com.android.internal.R.drawable.ic_media_pause;
                 imageDescId = com.android.internal.R.string.lockscreen_transport_pause_description;
-                showIfHidden = true;
                 break;
 
             case RemoteControlClient.PLAYSTATE_BUFFERING:
                 imageResId = com.android.internal.R.drawable.ic_media_stop;
                 imageDescId = com.android.internal.R.string.lockscreen_transport_stop_description;
-                showIfHidden = true;
                 break;
 
             case RemoteControlClient.PLAYSTATE_PAUSED:
             default:
                 imageResId = com.android.internal.R.drawable.ic_media_play;
                 imageDescId = com.android.internal.R.string.lockscreen_transport_play_description;
-                showIfHidden = false;
                 break;
         }
         mBtnPlay.setImageResource(imageResId);
         mBtnPlay.setContentDescription(getResources().getString(imageDescId));
-        if (showIfHidden) {
-            show();
-        }
         mCurrentPlayState = state;
+        mTransportCallback.onPlayStateChanged();
     }
 
     static class SavedState extends BaseSavedState {
-        boolean wasShowing;
+        boolean clientPresent;
 
         SavedState(Parcelable superState) {
             super(superState);
@@ -402,13 +401,13 @@
 
         private SavedState(Parcel in) {
             super(in);
-            this.wasShowing = in.readInt() != 0;
+            this.clientPresent = in.readInt() != 0;
         }
 
         @Override
         public void writeToParcel(Parcel out, int flags) {
             super.writeToParcel(out, flags);
-            out.writeInt(this.wasShowing ? 1 : 0);
+            out.writeInt(this.clientPresent ? 1 : 0);
         }
 
         public static final Parcelable.Creator<SavedState> CREATOR
@@ -425,24 +424,23 @@
 
     @Override
     public Parcelable onSaveInstanceState() {
-        if (DEBUG) Log.v(TAG, "onSaveInstanceState()");
         Parcelable superState = super.onSaveInstanceState();
         SavedState ss = new SavedState(superState);
-        ss.wasShowing = getVisibility() == View.VISIBLE;
+        ss.clientPresent = mMusicClientPresent;
         return ss;
     }
 
     @Override
     public void onRestoreInstanceState(Parcelable state) {
-        if (DEBUG) Log.v(TAG, "onRestoreInstanceState()");
         if (!(state instanceof SavedState)) {
             super.onRestoreInstanceState(state);
             return;
         }
         SavedState ss = (SavedState) state;
         super.onRestoreInstanceState(ss.getSuperState());
-        if (ss.wasShowing) {
-            show();
+        if (ss.clientPresent) {
+            if (DEBUG) Log.v(TAG, "Reattaching client because it was attached");
+            onListenerAttached();
         }
     }
 
@@ -458,7 +456,6 @@
         }
         if (keyCode != -1) {
             sendMediaButtonClick(keyCode);
-            userActivity();
         }
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
index 33ff71e..3ffd43f 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
@@ -48,7 +48,7 @@
  * reported to this class by the current {@link KeyguardViewBase}.
  */
 public class KeyguardViewManager {
-    private final static boolean DEBUG = false;
+    private final static boolean DEBUG = true;
     private static String TAG = "KeyguardViewManager";
     public static boolean USE_UPPER_CASE = true;
 
@@ -201,9 +201,6 @@
         if (v != null) {
             mKeyguardHost.removeView(v);
         }
-        // TODO: Remove once b/7094175 is fixed
-        Slog.d(TAG, "inflateKeyguardView: b/7094175 mContext.config="
-                + mContext.getResources().getConfiguration());
         final LayoutInflater inflater = LayoutInflater.from(mContext);
         View view = inflater.inflate(R.layout.keyguard_host_view, mKeyguardHost, true);
         mKeyguardView = (KeyguardHostView) view.findViewById(R.id.keyguard_host_view);
@@ -359,6 +356,12 @@
 
         if (mKeyguardHost != null) {
             mKeyguardHost.setVisibility(View.GONE);
+
+            // We really only want to preserve keyguard state for configuration changes. Hence
+            // we should clear state of widgets (e.g. Music) when we hide keyguard so it can
+            // start with a fresh state when we return.
+            mStateContainer.clear();
+
             // Don't do this right away, so we can let the view continue to animate
             // as it goes away.
             if (mKeyguardView != null) {
diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java
index e7cd279..18182cd 100755
--- a/services/java/com/android/server/BluetoothManagerService.java
+++ b/services/java/com/android/server/BluetoothManagerService.java
@@ -4,6 +4,7 @@
 
 package com.android.server;
 
+import android.app.ActivityManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.IBluetooth;
 import android.bluetooth.IBluetoothCallback;
@@ -17,17 +18,21 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.os.Binder;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Message;
+import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
-import android.os.Binder;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
-import java.util.List;
 import java.util.ArrayList;
+import java.util.List;
 class BluetoothManagerService extends IBluetoothManager.Stub {
     private static final String TAG = "BluetoothManagerService";
     private static final boolean DBG = true;
@@ -42,6 +47,8 @@
     private static final int TIMEOUT_SAVE_MS = 500; //Maximum msec to wait for a save
     //Maximum msec to wait for service restart
     private static final int SERVICE_RESTART_TIME_MS = 200;
+    //Maximum msec to delay MESSAGE_USER_SWITCHED
+    private static final int USER_SWITCHED_TIME_MS = 200;
 
     private static final int MESSAGE_ENABLE = 1;
     private static final int MESSAGE_DISABLE = 2;
@@ -57,6 +64,7 @@
     private static final int MESSAGE_TIMEOUT_UNBIND =101;
     private static final int MESSAGE_GET_NAME_AND_ADDRESS=200;
     private static final int MESSAGE_SAVE_NAME_AND_ADDRESS=201;
+    private static final int MESSAGE_USER_SWITCHED = 300;
     private static final int MAX_SAVE_RETRIES=3;
 
     private final Context mContext;
@@ -72,6 +80,10 @@
     private boolean mBinding;
     private boolean mUnbinding;
     private boolean mQuietEnable = false;
+    private boolean mEnable;
+    private int mState;
+    private HandlerThread mThread;
+    private final BluetoothHandler mHandler;
 
     private void registerForAirplaneMode(IntentFilter filter) {
         final ContentResolver resolver = mContext.getContentResolver();
@@ -106,23 +118,32 @@
                 }
             } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
                 if (isAirplaneModeOn()) {
-                        // disable without persisting the setting
-                        handleDisable(false);
-                } else {
-                    if (isBluetoothPersistedStateOn()) {
-                        // enable without persisting the setting
-                        handleEnable(false, false);
-                    }
+                    // disable without persisting the setting
+                    mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE,
+                           0, 0));
+                } else if (isBluetoothPersistedStateOn()) {
+                    // enable without persisting the setting
+                    mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE,
+                           0, 0));
                 }
+            } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+                mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_SWITCHED,
+                       intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
             }
         }
     };
 
     BluetoothManagerService(Context context) {
+        mThread = new HandlerThread("BluetoothManager");
+        mThread.start();
+        mHandler = new BluetoothHandler(mThread.getLooper());
+
         mContext = context;
         mBluetooth = null;
         mBinding = false;
         mUnbinding = false;
+        mEnable = false;
+        mState = BluetoothAdapter.STATE_OFF;
         mAddress = null;
         mName = null;
         mContentResolver = context.getContentResolver();
@@ -130,6 +151,7 @@
         mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>();
         IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
         filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
+        filter.addAction(Intent.ACTION_USER_SWITCHED);
         registerForAirplaneMode(filter);
         mContext.registerReceiver(mReceiver, filter);
         boolean airplaneModeOn = isAirplaneModeOn();
@@ -139,7 +161,7 @@
         if (bluetoothOn) {
             //Enable
             if (DBG) Log.d(TAG, "Auto-enabling Bluetooth.");
-            enable();
+            enableHelper();
         } else if (!isNameAndAddressSet()) {
             //Sync the Bluetooth name and address from the Bluetooth Adapter
             if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address...");
@@ -251,6 +273,11 @@
     }
 
     public boolean isEnabled() {
+        if (!checkIfCallerIsForegroundUser()) {
+            Log.w(TAG,"isEnabled(): not allowed for non-active user");
+            return false;
+        }
+
         synchronized(mConnection) {
             try {
                 return (mBluetooth != null && mBluetooth.isEnabled());
@@ -266,10 +293,6 @@
             Log.d(TAG,"getNameAndAddress(): mBluetooth = " + mBluetooth +
                   " mBinding = " + mBinding);
         }
-        synchronized(mConnection) {
-            if (mBinding) return;
-            if (mConnection == null) mBinding = true;
-        }
         Message msg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
         mHandler.sendMessage(msg);
     }
@@ -277,21 +300,19 @@
     {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH ADMIN permission");
+
+        if (!checkIfCallerIsForegroundUser()) {
+            Log.w(TAG,"enableNoAutoConnect(): not allowed for non-active user");
+            return false;
+        }
+
         if (DBG) {
             Log.d(TAG,"enableNoAutoConnect():  mBluetooth =" + mBluetooth +
                     " mBinding = " + mBinding);
         }
-        if (Binder.getCallingUid() != android.os.Process.NFC_UID) {
+        if (Binder.getCallingUid() != Process.NFC_UID) {
             throw new SecurityException("no permission to enable Bluetooth quietly");
         }
-        synchronized(mConnection) {
-            if (mBinding) {
-                Log.w(TAG,"enableNoAutoConnect(): binding in progress. Returning..");
-                return true;
-            }
-            if (mConnection == null) mBinding = true;
-        }
-
         Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
         msg.arg1=0; //No persist
         msg.arg2=1; //Quiet mode
@@ -300,39 +321,28 @@
 
     }
     public boolean enable() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH ADMIN permission");
-        if (DBG) {
-            Log.d(TAG,"enable():  mBluetooth =" + mBluetooth +
-                    " mBinding = " + mBinding);
+        if (!checkIfCallerIsForegroundUser()) {
+            Log.w(TAG,"enable(): not allowed for non-active user");
+            return false;
         }
 
-        synchronized(mConnection) {
-            if (mBinding) {
-                Log.w(TAG,"enable(): binding in progress. Returning..");
-                return true;
-            }
-            if (mConnection == null) mBinding = true;
-        }
-
-        Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
-        msg.arg1=1; //persist
-        msg.arg2=0; //No Quiet Mode
-        mHandler.sendMessage(msg);
-        return true;
+        return enableHelper();
     }
 
     public boolean disable(boolean persist) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH ADMIN permissicacheNameAndAddresson");
+
+        if (!checkIfCallerIsForegroundUser()) {
+            Log.w(TAG,"disable(): not allowed for non-active user");
+            return false;
+        }
+
         if (DBG) {
             Log.d(TAG,"disable(): mBluetooth = " + mBluetooth +
                 " mBinding = " + mBinding);
         }
 
-        synchronized(mConnection) {
-             if (mBluetooth == null) return false;
-        }
         Message msg = mHandler.obtainMessage(MESSAGE_DISABLE);
         msg.arg1=(persist?1:0);
         mHandler.sendMessage(msg);
@@ -348,13 +358,13 @@
         synchronized (mConnection) {
             if (mUnbinding) return;
             mUnbinding = true;
-            if (mConnection != null) {
+            if (mBluetooth != null) {
                 if (!mConnection.isGetNameAddressOnly()) {
                     //Unregister callback object
                     try {
                         mBluetooth.unregisterCallback(mBluetoothCallback);
                     } catch (RemoteException re) {
-                        Log.e(TAG, "Unable to register BluetoothCallback",re);
+                        Log.e(TAG, "Unable to unregister BluetoothCallback",re);
                     }
                 }
                 if (DBG) Log.d(TAG, "Sending unbind request.");
@@ -362,6 +372,7 @@
                 //Unbind
                 mContext.unbindService(mConnection);
                 mUnbinding = false;
+                mBinding = false;
             } else {
                 mUnbinding=false;
             }
@@ -382,6 +393,24 @@
     }
 
     /**
+     * Inform BluetoothAdapter instances that Adapter service is up
+     */
+    private void sendBluetoothServiceUpCallback() {
+        if (!mConnection.isGetNameAddressOnly()) {
+            if (DBG) Log.d(TAG,"Calling onBluetoothServiceUp callbacks");
+            int n = mCallbacks.beginBroadcast();
+            Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
+            for (int i=0; i <n;i++) {
+                try {
+                    mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
+                }  catch (RemoteException e) {
+                    Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
+                }
+            }
+            mCallbacks.finishBroadcast();
+        }
+    }
+    /**
      * Inform BluetoothAdapter instances that Adapter service is down
      */
     private void sendBluetoothServiceDownCallback() {
@@ -402,6 +431,12 @@
     public String getAddress() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH ADMIN permission");
+
+        if (!checkIfCallerIsForegroundUser()) {
+            Log.w(TAG,"getAddress(): not allowed for non-active user");
+            return mAddress;
+        }
+
         synchronized(mConnection) {
             if (mBluetooth != null) {
                 try {
@@ -420,6 +455,12 @@
     public String getName() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH ADMIN permission");
+
+        if (!checkIfCallerIsForegroundUser()) {
+            Log.w(TAG,"getName(): not allowed for non-active user");
+            return mName;
+        }
+
         synchronized(mConnection) {
             if (mBluetooth != null) {
                 try {
@@ -464,7 +505,11 @@
 
     private BluetoothServiceConnection mConnection = new BluetoothServiceConnection();
 
-    private final Handler mHandler = new Handler() {
+    private class BluetoothHandler extends Handler {
+        public BluetoothHandler(Looper looper) {
+            super(looper);
+        }
+
         @Override
         public void handleMessage(Message msg) {
             if (DBG) Log.d (TAG, "Message: " + msg.what);
@@ -473,7 +518,7 @@
                     if (DBG) Log.d(TAG,"MESSAGE_GET_NAME_AND_ADDRESS");
                     synchronized(mConnection) {
                         //Start bind request
-                        if (mBluetooth == null) {
+                        if ((mBluetooth == null) && (!mBinding)) {
                             if (DBG) Log.d(TAG, "Binding to service to get name and address");
                             mConnection.setGetNameAddressOnly(true);
                             //Start bind timeout and bind
@@ -484,11 +529,20 @@
                                   Context.BIND_AUTO_CREATE, UserHandle.USER_CURRENT)) {
                                 mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
                                 Log.e(TAG, "fail to bind to: " + IBluetooth.class.getName());
+                            } else {
+                                mBinding = true;
                             }
                         }
                         else {
                             Message saveMsg= mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
-                            mHandler.sendMessage(saveMsg);
+                            saveMsg.arg1 = 0;
+                            if (mBluetooth != null) {
+                                mHandler.sendMessage(saveMsg);
+                            } else {
+                                // if enable is also called to bind the service
+                                // wait for MESSAGE_BLUETOOTH_SERVICE_CONNECTED
+                                mHandler.sendMessageDelayed(saveMsg, TIMEOUT_SAVE_MS);
+                            }
                         }
                     }
                     break;
@@ -508,8 +562,9 @@
 
                             if (name != null && address != null) {
                                 storeNameAndAddress(name,address);
-                                sendBluetoothServiceDownCallback();
-                                unbindAndFinish();
+                                if (mConnection.isGetNameAddressOnly()) {
+                                    unbindAndFinish();
+                                }
                             } else {
                                 if (msg.arg1 < MAX_SAVE_RETRIES) {
                                     Message retryMsg = mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
@@ -518,10 +573,17 @@
                                     mHandler.sendMessageDelayed(retryMsg, TIMEOUT_SAVE_MS);
                                 } else {
                                     Log.w(TAG,"Maximum name/address remote retrieval retry exceeded");
-                                    sendBluetoothServiceDownCallback();
-                                    unbindAndFinish();
+                                    if (mConnection.isGetNameAddressOnly()) {
+                                        unbindAndFinish();
+                                    }
                                 }
                             }
+                        } else {
+                            // rebind service by Request GET NAME AND ADDRESS
+                            // if service is unbinded by disable or
+                            // MESSAGE_BLUETOOTH_SERVICE_CONNECTED is not received
+                            Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
+                            mHandler.sendMessage(getMsg);
                         }
                     }
                     break;
@@ -530,12 +592,22 @@
                     if (DBG) {
                         Log.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth);
                     }
-
+                    mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
+                    mEnable = true;
                     handleEnable(msg.arg1 == 1, msg.arg2 ==1);
                     break;
 
                 case MESSAGE_DISABLE:
-                    handleDisable(msg.arg1 == 1);
+                    mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
+                    if (mEnable && mBluetooth != null) {
+                        waitForOnOff(true, false);
+                        mEnable = false;
+                        handleDisable(msg.arg1 == 1);
+                        waitForOnOff(false, false);
+                    } else {
+                        mEnable = false;
+                        handleDisable(msg.arg1 == 1);
+                    }
                     break;
 
                 case MESSAGE_REGISTER_ADAPTER:
@@ -580,27 +652,26 @@
                             //Request GET NAME AND ADDRESS
                             Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
                             mHandler.sendMessage(getMsg);
-                            return;
+                            if (!mEnable) return;
                         }
 
+                        mConnection.setGetNameAddressOnly(false);
                         //Register callback object
                         try {
                             mBluetooth.registerCallback(mBluetoothCallback);
                         } catch (RemoteException re) {
                             Log.e(TAG, "Unable to register BluetoothCallback",re);
                         }
-
                         //Inform BluetoothAdapter instances that service is up
-                        int n = mCallbacks.beginBroadcast();
-                        Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
-                        for (int i=0; i <n;i++) {
+                        sendBluetoothServiceUpCallback();
+
+                        //Check if name and address is loaded if not get it first.
+                        if (!isNameAndAddressSet()) {
                             try {
-                                mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
-                            } catch (RemoteException e) {
-                                Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
-                            }
+                                storeNameAndAddress(mBluetooth.getName(),
+                                                    mBluetooth.getAddress());
+                            } catch (RemoteException e) {Log.e(TAG, "", e);};
                         }
-                        mCallbacks.finishBroadcast();
 
                         //Do enable request
                         try {
@@ -619,12 +690,19 @@
                             Log.e(TAG,"Unable to call enable()",e);
                         }
                     }
+
+                    if (!mEnable) {
+                        waitForOnOff(true, false);
+                        handleDisable(false);
+                        waitForOnOff(false, false);
+                    }
                     break;
                 }
                 case MESSAGE_TIMEOUT_BIND: {
                     Log.e(TAG, "MESSAGE_TIMEOUT_BIND");
                     synchronized(mConnection) {
                         mBinding = false;
+                        mEnable = false;
                     }
                     break;
                 }
@@ -633,51 +711,37 @@
                     int prevState = msg.arg1;
                     int newState = msg.arg2;
                     if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: prevState = " + prevState + ", newState=" + newState);
-                    if (prevState != newState) {
-                        //Notify all proxy objects first of adapter state change
-                        if (newState == BluetoothAdapter.STATE_ON || newState == BluetoothAdapter.STATE_OFF) {
-                            boolean isUp = (newState==BluetoothAdapter.STATE_ON);
-                            sendBluetoothStateCallback(isUp);
-
-                            //If Bluetooth is off, send service down event to proxy objects, and unbind
-                            if (!isUp) {
-                                sendBluetoothServiceDownCallback();
-                                unbindAndFinish();
-                            }
-                        }
-
-                        //Send broadcast message to everyone else
-                        Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
-                        intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
-                        intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
-                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-                        if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState);
-                        mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
-                                BLUETOOTH_PERM);
-                    }
+                    mState = newState;
+                    bluetoothStateChangeHandler(prevState, newState);
                     break;
                 }
                 case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
                 {
-                    if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED");
-                    sendBluetoothServiceDownCallback();
-
-                    // Send BT state broadcast to update
-                    // the BT icon correctly
-                    Message stateChangeMsg = mHandler.obtainMessage(
-                        MESSAGE_BLUETOOTH_STATE_CHANGE);
-                    stateChangeMsg.arg1 = BluetoothAdapter.STATE_ON;
-                    stateChangeMsg.arg2 =
-                        BluetoothAdapter.STATE_TURNING_OFF;
-                    mHandler.sendMessage(stateChangeMsg);
+                    Log.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED");
                     synchronized(mConnection) {
+                        // if service is unbinded already, do nothing and return
+                        if (mBluetooth == null) return;
                         mBluetooth = null;
                     }
-                    // Send a Bluetooth Restart message
-                    Message restartMsg = mHandler.obtainMessage(
-                        MESSAGE_RESTART_BLUETOOTH_SERVICE);
-                    mHandler.sendMessageDelayed(restartMsg,
-                        SERVICE_RESTART_TIME_MS);
+
+                    if (mEnable) {
+                        mEnable = false;
+                        // Send a Bluetooth Restart message
+                        Message restartMsg = mHandler.obtainMessage(
+                            MESSAGE_RESTART_BLUETOOTH_SERVICE);
+                        mHandler.sendMessageDelayed(restartMsg,
+                            SERVICE_RESTART_TIME_MS);
+                    }
+
+                    if (!mConnection.isGetNameAddressOnly()) {
+                        sendBluetoothServiceDownCallback();
+
+                        // Send BT state broadcast to update
+                        // the BT icon correctly
+                        bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
+                                                    BluetoothAdapter.STATE_TURNING_OFF);
+                        mState = BluetoothAdapter.STATE_OFF;
+                    }
                     break;
                 }
                 case MESSAGE_RESTART_BLUETOOTH_SERVICE:
@@ -687,6 +751,7 @@
                     /* Enable without persisting the setting as
                      it doesnt change when IBluetooth
                      service restarts */
+                    mEnable = true;
                     handleEnable(false, mQuietEnable);
                     break;
                 }
@@ -699,9 +764,66 @@
                     }
                     break;
                 }
+
+                case MESSAGE_USER_SWITCHED:
+                {
+                    if (DBG) {
+                        Log.d(TAG, "MESSAGE_USER_SWITCHED");
+                    }
+                    mHandler.removeMessages(MESSAGE_USER_SWITCHED);
+                    /* disable and enable BT when detect a user switch */
+                    if (mEnable && mBluetooth != null) {
+                        synchronized (mConnection) {
+                            if (mBluetooth != null) {
+                                //Unregister callback object
+                                try {
+                                    mBluetooth.unregisterCallback(mBluetoothCallback);
+                                } catch (RemoteException re) {
+                                    Log.e(TAG, "Unable to unregister",re);
+                                }
+                            }
+                        }
+                        mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
+
+                        waitForOnOff(true, false);
+
+                        bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON);
+
+                        // disable
+                        handleDisable(false);
+
+                        waitForOnOff(false, true);
+
+                        bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
+                                                    BluetoothAdapter.STATE_OFF);
+                        mState = BluetoothAdapter.STATE_OFF;
+                        sendBluetoothServiceDownCallback();
+                        synchronized (mConnection) {
+                            if (mBluetooth != null) {
+                                mBluetooth = null;
+                                //Unbind
+                                mContext.unbindService(mConnection);
+                            }
+                        }
+                        SystemClock.sleep(100);
+
+                        // enable
+                        handleEnable(false, mQuietEnable);
+		    } else if (mBinding || mBluetooth != null) {
+                        Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED);
+                        userMsg.arg2 = 1 + msg.arg2;
+                        // if user is switched when service is being binding
+                        // delay sending MESSAGE_USER_SWITCHED
+                        mHandler.sendMessageDelayed(userMsg, USER_SWITCHED_TIME_MS);
+                        if (DBG) {
+                            Log.d(TAG, "delay MESSAGE_USER_SWITCHED " + userMsg.arg2);
+                        }
+		    }
+                    break;
+                }
             }
         }
-    };
+    }
 
     private void handleEnable(boolean persist, boolean quietMode) {
         if (persist) {
@@ -711,18 +833,35 @@
         mQuietEnable = quietMode;
 
         synchronized(mConnection) {
-            if (mBluetooth == null) {
+            if ((mBluetooth == null) && (!mBinding)) {
                 //Start bind timeout and bind
                 Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
                 mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
                 mConnection.setGetNameAddressOnly(false);
                 Intent i = new Intent(IBluetooth.class.getName());
-                if (!mContext.bindService(i, mConnection, Context.BIND_AUTO_CREATE,
+                if (!mContext.bindService(i, mConnection,Context.BIND_AUTO_CREATE,
                                           UserHandle.USER_CURRENT)) {
                     mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
                     Log.e(TAG, "Fail to bind to: " + IBluetooth.class.getName());
+                } else {
+                    mBinding = true;
                 }
-            } else {
+            } else if (mBluetooth != null) {
+                if (mConnection.isGetNameAddressOnly()) {
+                    // if GetNameAddressOnly is set, we can clear this flag,
+                    // so the service won't be unbind
+                    // after name and address are saved
+                    mConnection.setGetNameAddressOnly(false);
+                    //Register callback object
+                    try {
+                        mBluetooth.registerCallback(mBluetoothCallback);
+                    } catch (RemoteException re) {
+                        Log.e(TAG, "Unable to register BluetoothCallback",re);
+                    }
+                    //Inform BluetoothAdapter instances that service is up
+                    sendBluetoothServiceUpCallback();
+                }
+
                 //Check if name and address is loaded if not get it first.
                 if (!isNameAndAddressSet()) {
                     try {
@@ -751,12 +890,14 @@
     }
 
     private void handleDisable(boolean persist) {
+        if (persist) {
+            persistBluetoothSetting(false);
+        }
+
         synchronized(mConnection) {
-            if (mBluetooth != null ) {
-                if (persist) {
-                    persistBluetoothSetting(false);
-                }
-                mConnection.setGetNameAddressOnly(false);
+            // don't need to disable if GetNameAddressOnly is set,
+            // service will be unbinded after Name and Address are saved
+            if ((mBluetooth != null) && (!mConnection.isGetNameAddressOnly())) {
                 if (DBG) Log.d(TAG,"Sending off request.");
 
                 try {
@@ -769,4 +910,102 @@
             }
         }
     }
+
+    private boolean checkIfCallerIsForegroundUser() {
+        int foregroundUser;
+        int callingUser = UserHandle.getCallingUserId();
+        long callingIdentity = Binder.clearCallingIdentity();
+        boolean valid = false;
+        try {
+            foregroundUser = ActivityManager.getCurrentUser();
+            valid = (callingUser == foregroundUser);
+            if (DBG) {
+                Log.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid
+                    + " callingUser=" + callingUser
+                    + " foregroundUser=" + foregroundUser);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentity);
+        }
+        return valid;
+    }
+
+    private boolean enableHelper() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                "Need BLUETOOTH ADMIN permission");
+        if (DBG) {
+            Log.d(TAG,"enable():  mBluetooth =" + mBluetooth +
+                    " mBinding = " + mBinding);
+        }
+
+        Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
+        msg.arg1=1; //persist
+        msg.arg2=0; //No Quiet Mode
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    private void bluetoothStateChangeHandler(int prevState, int newState) {
+        if (prevState != newState) {
+            //Notify all proxy objects first of adapter state change
+            if (newState == BluetoothAdapter.STATE_ON || newState == BluetoothAdapter.STATE_OFF) {
+                boolean isUp = (newState==BluetoothAdapter.STATE_ON);
+                sendBluetoothStateCallback(isUp);
+
+                //If Bluetooth is off, send service down event to proxy objects, and unbind
+                if (!isUp) {
+                    //Only unbind with mEnable flag not set
+                    //For race condition: disable and enable back-to-back
+                    //Avoid unbind right after enable due to callback from disable
+                    if ((!mEnable) && (mBluetooth != null)) {
+                        sendBluetoothServiceDownCallback();
+                        unbindAndFinish();
+                    }
+                }
+            }
+
+            //Send broadcast message to everyone else
+            Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
+            intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
+            intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+            if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                    BLUETOOTH_PERM);
+        }
+    }
+
+    /**
+     *  if on is true, wait for state become ON
+     *  if off is true, wait for state become OFF
+     *  if both on and off are false, wait for state not ON
+     */
+    private boolean waitForOnOff(boolean on, boolean off) {
+        int i = 0;
+        while (i < 10) {
+            synchronized(mConnection) {
+                try {
+                    if (mBluetooth == null) break;
+                    if (on) {
+                        if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true;
+                    } else if (off) {
+                        if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true;
+		    } else {
+                        if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true;
+		    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "getState()", e);
+                    break;
+                }
+            }
+            if (on || off) {
+                SystemClock.sleep(300);
+	    } else {
+                SystemClock.sleep(50);
+	    }
+            i++;
+        }
+        Log.e(TAG,"waitForOnOff time out");
+        return false;
+    }
 }
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index ca28e4c..c5016e6 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -308,6 +308,7 @@
             addProviderLocked(fusedLocationProvider);
             mProxyProviders.add(fusedLocationProvider);
             mEnabledProviders.add(fusedLocationProvider.getName());
+            mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedLocationProvider);
         } else {
             Slog.e(TAG, "no fused location provider found",
                     new IllegalStateException("Location service needs a fused location provider"));
diff --git a/services/java/com/android/server/WiredAccessoryManager.java b/services/java/com/android/server/WiredAccessoryManager.java
index 63e8895..d5c9c8f 100644
--- a/services/java/com/android/server/WiredAccessoryManager.java
+++ b/services/java/com/android/server/WiredAccessoryManager.java
@@ -152,7 +152,7 @@
                     break;
             }
 
-            updateLocked(NAME_H2W, headset);
+            updateLocked(NAME_H2W, (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC)) | headset);
         }
     }
 
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index 7e3fdbd..35999ea 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -248,8 +248,9 @@
         synchronized (r.stats.getBatteryStats()) {
             r.stats.startRunningLocked();
         }
-        if (!bringUpServiceLocked(r, service.getFlags(), false)) {
-            return new ComponentName("!", "Service process is bad");
+        String error = bringUpServiceLocked(r, service.getFlags(), false);
+        if (error != null) {
+            return new ComponentName("!!", error);
         }
         return r.name;
     }
@@ -518,7 +519,7 @@
 
             if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                 s.lastActivity = SystemClock.uptimeMillis();
-                if (!bringUpServiceLocked(s, service.getFlags(), false)) {
+                if (bringUpServiceLocked(s, service.getFlags(), false) != null) {
                     return 0;
                 }
             }
@@ -964,19 +965,19 @@
         return true;
     }
 
-    private final boolean bringUpServiceLocked(ServiceRecord r,
+    private final String bringUpServiceLocked(ServiceRecord r,
             int intentFlags, boolean whileRestarting) {
         //Slog.i(TAG, "Bring up service:");
         //r.dump("  ");
 
         if (r.app != null && r.app.thread != null) {
             sendServiceArgsLocked(r, false);
-            return true;
+            return null;
         }
 
         if (!whileRestarting && r.restartDelay > 0) {
             // If waiting for a restart, then do nothing.
-            return true;
+            return null;
         }
 
         if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up " + r + " " + r.intent);
@@ -988,12 +989,13 @@
         // Make sure that the user who owns this service is started.  If not,
         // we don't want to allow it to run.
         if (mAm.mStartedUsers.get(r.userId) == null) {
-            Slog.w(TAG, "Unable to launch app "
+            String msg = "Unable to launch app "
                     + r.appInfo.packageName + "/"
                     + r.appInfo.uid + " for service "
-                    + r.intent.getIntent() + ": user " + r.userId + " is stopped");
+                    + r.intent.getIntent() + ": user " + r.userId + " is stopped";
+            Slog.w(TAG, msg);
             bringDownServiceLocked(r, true);
-            return false;
+            return msg;
         }
 
         // Service is now being launched, its package can't be stopped.
@@ -1018,7 +1020,7 @@
                 try {
                     app.addPackage(r.appInfo.packageName);
                     realStartServiceLocked(r, app);
-                    return true;
+                    return null;
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Exception when starting service " + r.shortName, e);
                 }
@@ -1041,12 +1043,13 @@
         if (app == null) {
             if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                     "service", r.name, false, isolated)) == null) {
-                Slog.w(TAG, "Unable to launch app "
+                String msg = "Unable to launch app "
                         + r.appInfo.packageName + "/"
                         + r.appInfo.uid + " for service "
-                        + r.intent.getIntent() + ": process is bad");
+                        + r.intent.getIntent() + ": process is bad";
+                Slog.w(TAG, msg);
                 bringDownServiceLocked(r, true);
-                return false;
+                return msg;
             }
             if (isolated) {
                 r.isolatedProc = app;
@@ -1057,7 +1060,7 @@
             mPendingServices.add(r);
         }
 
-        return true;
+        return null;
     }
 
     private final void requestServiceBindingsLocked(ServiceRecord r) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index c2aa3a5..daed0a2 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3585,7 +3585,7 @@
                         Slog.w(TAG, "Failed trying to unstop package "
                                 + packageName + ": " + e);
                     }
-                    if (isUserRunningLocked(user)) {
+                    if (isUserRunningLocked(user, false)) {
                         forceStopPackageLocked(packageName, pkgUid);
                     }
                 }
@@ -3739,7 +3739,8 @@
     private void forceStopUserLocked(int userId) {
         forceStopPackageLocked(null, -1, false, false, true, false, userId);
         Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                | Intent.FLAG_RECEIVER_FOREGROUND);
         intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
         broadcastIntentLocked(null, null, intent,
                 null, null, 0, null, null, null,
@@ -7904,6 +7905,19 @@
                 broadcastIntentLocked(null, null, intent,
                         null, null, 0, null, null, null,
                         false, false, MY_PID, Process.SYSTEM_UID, mCurrentUserId);
+                intent = new Intent(Intent.ACTION_USER_STARTING);
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId);
+                broadcastIntentLocked(null, null, intent,
+                        null, new IIntentReceiver.Stub() {
+                            @Override
+                            public void performReceive(Intent intent, int resultCode, String data,
+                                    Bundle extras, boolean ordered, boolean sticky, int sendingUser)
+                                    throws RemoteException {
+                            }
+                        }, 0, null, null,
+                        android.Manifest.permission.INTERACT_ACROSS_USERS,
+                        false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -8879,7 +8893,7 @@
                 pw.println("  [-a] [-c] [-h] [cmd] ...");
                 pw.println("  cmd may be one of:");
                 pw.println("    a[ctivities]: activity stack state");
-                pw.println("    b[roadcasts] [PACKAGE_NAME]: broadcast state");
+                pw.println("    b[roadcasts] [PACKAGE_NAME] [history [-s]]: broadcast state");
                 pw.println("    i[ntents] [PACKAGE_NAME]: pending intent state");
                 pw.println("    p[rocesses] [PACKAGE_NAME]: process state");
                 pw.println("    o[om]: out of memory management");
@@ -9338,6 +9352,12 @@
             pw.print("    User #"); pw.print(uss.mHandle.getIdentifier());
                     pw.print(": "); uss.dump("", pw);
         }
+        pw.print("  mStartedUserArray: [");
+        for (int i=0; i<mStartedUserArray.length; i++) {
+            if (i > 0) pw.print(", ");
+            pw.print(mStartedUserArray[i]);
+        }
+        pw.println("]");
         pw.print("  mUserLru: [");
         for (int i=0; i<mUserLru.size(); i++) {
             if (i > 0) pw.print(", ");
@@ -9707,6 +9727,9 @@
         boolean onlyHistory = false;
 
         if ("history".equals(dumpPackage)) {
+            if (opti < args.length && "-s".equals(args[opti])) {
+                dumpAll = false;
+            }
             onlyHistory = true;
             dumpPackage = null;
         }
@@ -14106,11 +14129,14 @@
                 mWindowManager.startFreezingScreen(R.anim.screen_user_exit,
                         R.anim.screen_user_enter);
 
+                boolean needStart = false;
+
                 // If the user we are switching to is not currently started, then
                 // we need to start it now.
                 if (mStartedUsers.get(userId) == null) {
                     mStartedUsers.put(userId, new UserStartedState(new UserHandle(userId), false));
                     updateStartedUserArrayLocked();
+                    needStart = true;
                 }
 
                 mCurrentUserId = userId;
@@ -14134,10 +14160,14 @@
                     // so we can just fairly silently bring the user back from
                     // the almost-dead.
                     uss.mState = UserStartedState.STATE_RUNNING;
+                    updateStartedUserArrayLocked();
+                    needStart = true;
                 } else if (uss.mState == UserStartedState.STATE_SHUTDOWN) {
                     // This means ACTION_SHUTDOWN has been sent, so we will
                     // need to treat this as a new boot of the user.
                     uss.mState = UserStartedState.STATE_BOOTING;
+                    updateStartedUserArrayLocked();
+                    needStart = true;
                 }
 
                 mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
@@ -14146,17 +14176,19 @@
                         oldUserId, userId, uss));
                 mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
                         oldUserId, userId, uss), USER_SWITCH_TIMEOUT);
-                Intent intent = new Intent(Intent.ACTION_USER_STARTED);
-                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
-                        | Intent.FLAG_RECEIVER_FOREGROUND);
-                intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-                broadcastIntentLocked(null, null, intent,
-                        null, null, 0, null, null, null,
-                        false, false, MY_PID, Process.SYSTEM_UID, userId);
+                if (needStart) {
+                    Intent intent = new Intent(Intent.ACTION_USER_STARTED);
+                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                            | Intent.FLAG_RECEIVER_FOREGROUND);
+                    intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+                    broadcastIntentLocked(null, null, intent,
+                            null, null, 0, null, null, null,
+                            false, false, MY_PID, Process.SYSTEM_UID, userId);
+                }
 
                 if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
                     if (userId != 0) {
-                        intent = new Intent(Intent.ACTION_USER_INITIALIZE);
+                        Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
                         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                         broadcastIntentLocked(null, null, intent, null,
                                 new IIntentReceiver.Stub() {
@@ -14180,6 +14212,21 @@
 
                 getUserManagerLocked().userForeground(userId);
                 sendUserSwitchBroadcastsLocked(oldUserId, userId);
+                if (needStart) {
+                    Intent intent = new Intent(Intent.ACTION_USER_STARTING);
+                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+                    broadcastIntentLocked(null, null, intent,
+                            null, new IIntentReceiver.Stub() {
+                                @Override
+                                public void performReceive(Intent intent, int resultCode, String data,
+                                        Bundle extras, boolean ordered, boolean sticky, int sendingUser)
+                                        throws RemoteException {
+                                }
+                            }, 0, null, null,
+                            android.Manifest.permission.INTERACT_ACROSS_USERS,
+                            false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -14217,19 +14264,6 @@
                         null, null, 0, null, null,
                         android.Manifest.permission.MANAGE_USERS,
                         false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
-                intent = new Intent(Intent.ACTION_USER_STARTING);
-                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-                intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
-                broadcastIntentLocked(null, null, intent,
-                        null, new IIntentReceiver.Stub() {
-                            @Override
-                            public void performReceive(Intent intent, int resultCode, String data,
-                                    Bundle extras, boolean ordered, boolean sticky, int sendingUser)
-                                    throws RemoteException {
-                            }
-                        }, 0, null, null,
-                        android.Manifest.permission.INTERACT_ACROSS_USERS,
-                        false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -14318,8 +14352,7 @@
 
     void finishUserSwitch(UserStartedState uss) {
         synchronized (this) {
-            if ((uss.mState == UserStartedState.STATE_BOOTING
-                    || uss.mState == UserStartedState.STATE_SHUTDOWN)
+            if (uss.mState == UserStartedState.STATE_BOOTING
                     && mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) {
                 uss.mState = UserStartedState.STATE_RUNNING;
                 final int userId = uss.mHandle.getIdentifier();
@@ -14410,6 +14443,7 @@
         if (uss.mState != UserStartedState.STATE_STOPPING
                 && uss.mState != UserStartedState.STATE_SHUTDOWN) {
             uss.mState = UserStartedState.STATE_STOPPING;
+            updateStartedUserArrayLocked();
 
             long ident = Binder.clearCallingIdentity();
             try {
@@ -14514,7 +14548,7 @@
     }
 
     @Override
-    public boolean isUserRunning(int userId) {
+    public boolean isUserRunning(int userId, boolean orStopped) {
         if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
                 != PackageManager.PERMISSION_GRANTED) {
             String msg = "Permission Denial: isUserRunning() from pid="
@@ -14525,13 +14559,19 @@
             throw new SecurityException(msg);
         }
         synchronized (this) {
-            return isUserRunningLocked(userId);
+            return isUserRunningLocked(userId, orStopped);
         }
     }
 
-    boolean isUserRunningLocked(int userId) {
+    boolean isUserRunningLocked(int userId, boolean orStopped) {
         UserStartedState state = mStartedUsers.get(userId);
-        return state != null && state.mState != UserStartedState.STATE_STOPPING
+        if (state == null) {
+            return false;
+        }
+        if (orStopped) {
+            return true;
+        }
+        return state.mState != UserStartedState.STATE_STOPPING
                 && state.mState != UserStartedState.STATE_SHUTDOWN;
     }
 
@@ -14552,9 +14592,24 @@
     }
 
     private void updateStartedUserArrayLocked() {
-        mStartedUserArray = new int[mStartedUsers.size()];
+        int num = 0;
         for (int i=0; i<mStartedUsers.size();  i++) {
-            mStartedUserArray[i] = mStartedUsers.keyAt(i);
+            UserStartedState uss = mStartedUsers.valueAt(i);
+            // This list does not include stopping users.
+            if (uss.mState != UserStartedState.STATE_STOPPING
+                    && uss.mState != UserStartedState.STATE_SHUTDOWN) {
+                num++;
+            }
+        }
+        mStartedUserArray = new int[num];
+        num = 0;
+        for (int i=0; i<mStartedUsers.size();  i++) {
+            UserStartedState uss = mStartedUsers.valueAt(i);
+            if (uss.mState != UserStartedState.STATE_STOPPING
+                    && uss.mState != UserStartedState.STATE_SHUTDOWN) {
+                mStartedUserArray[num] = mStartedUsers.keyAt(i);
+                num++;
+            }
         }
     }
 
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index 95c22ec..f9630ae 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -54,6 +54,7 @@
     static final boolean DEBUG_MU = ActivityManagerService.DEBUG_MU;
 
     static final int MAX_BROADCAST_HISTORY = 25;
+    static final int MAX_BROADCAST_SUMMARY_HISTORY = 100;
 
     final ActivityManagerService mService;
 
@@ -93,6 +94,12 @@
             = new BroadcastRecord[MAX_BROADCAST_HISTORY];
 
     /**
+     * Summary of historical data of past broadcasts, for debugging.
+     */
+    final Intent[] mBroadcastSummaryHistory
+            = new Intent[MAX_BROADCAST_SUMMARY_HISTORY];
+
+    /**
      * Set when we current have a BROADCAST_INTENT_MSG in flight.
      */
     boolean mBroadcastsScheduled = false;
@@ -922,6 +929,9 @@
                 MAX_BROADCAST_HISTORY-1);
         r.finishTime = SystemClock.uptimeMillis();
         mBroadcastHistory[0] = r;
+        System.arraycopy(mBroadcastSummaryHistory, 0, mBroadcastSummaryHistory, 1,
+                MAX_BROADCAST_SUMMARY_HISTORY-1);
+        mBroadcastSummaryHistory[0] = r.intent;
     }
 
     final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
@@ -1006,8 +1016,9 @@
             }
         }
 
+        int i;
         boolean printed = false;
-        for (int i=0; i<MAX_BROADCAST_HISTORY; i++) {
+        for (i=0; i<MAX_BROADCAST_HISTORY; i++) {
             BroadcastRecord r = mBroadcastHistory[i];
             if (r == null) {
                 break;
@@ -1028,11 +1039,44 @@
                         pw.print(i); pw.println(":");
                 r.dump(pw, "    ");
             } else {
-                if (i >= 50) {
+                pw.print("  #"); pw.print(i); pw.print(": "); pw.println(r);
+                pw.print("    ");
+                pw.println(r.intent.toShortString(false, true, true, false));
+                Bundle bundle = r.intent.getExtras();
+                if (bundle != null) {
+                    pw.print("    extras: "); pw.println(bundle.toString());
+                }
+            }
+        }
+
+        if (dumpPackage == null) {
+            if (dumpAll) {
+                i = 0;
+                printed = false;
+            }
+            for (; i<MAX_BROADCAST_SUMMARY_HISTORY; i++) {
+                Intent intent = mBroadcastSummaryHistory[i];
+                if (intent == null) {
+                    break;
+                }
+                if (!printed) {
+                    if (needSep) {
+                        pw.println();
+                    }
+                    needSep = true;
+                    pw.println("  Historical broadcasts summary [" + mQueueName + "]:");
+                    printed = true;
+                }
+                if (!dumpAll && i >= 50) {
                     pw.println("  ...");
                     break;
                 }
-                pw.print("  #"); pw.print(i); pw.print(": "); pw.println(r);
+                pw.print("  #"); pw.print(i); pw.print(": ");
+                pw.println(intent.toShortString(false, true, true, false));
+                Bundle bundle = intent.getExtras();
+                if (bundle != null) {
+                    pw.print("    extras: "); pw.println(bundle.toString());
+                }
             }
         }
 
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index 85ec328..1cf5b9c 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -81,12 +81,10 @@
         final long now = SystemClock.uptimeMillis();
 
         pw.print(prefix); pw.print(this); pw.print(" to user "); pw.println(userId);
-        pw.print(prefix); pw.println(intent);
-        if (sticky) {
-            Bundle bundle = intent.getExtras();
-            if (bundle != null) {
-                pw.print(prefix); pw.print("extras: "); pw.println(bundle.toString());
-            }
+        pw.print(prefix); pw.println(intent.toInsecureString());
+        Bundle bundle = intent.getExtras();
+        if (bundle != null) {
+            pw.print(prefix); pw.print("extras: "); pw.println(bundle.toString());
         }
         pw.print(prefix); pw.print("caller="); pw.print(callerPackage); pw.print(" ");
                 pw.print(callerApp != null ? callerApp.toShortString() : "null");
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index 85bf17a..b76ad45 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -48,6 +48,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.SystemService;
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.provider.Settings;
@@ -81,6 +82,8 @@
     private static final int MSG_SANDMAN = 2;
     // Message: Sent when the screen on blocker is released.
     private static final int MSG_SCREEN_ON_BLOCKER_RELEASED = 3;
+    // Message: Sent to poll whether the boot animation has terminated.
+    private static final int MSG_CHECK_IF_BOOT_ANIMATION_FINISHED = 4;
 
     // Dirty bit: mWakeLocks changed
     private static final int DIRTY_WAKE_LOCKS = 1 << 0;
@@ -127,6 +130,7 @@
     private static final int WAKE_LOCK_SCREEN_DIM = 1 << 2;
     private static final int WAKE_LOCK_BUTTON_BRIGHT = 1 << 3;
     private static final int WAKE_LOCK_PROXIMITY_SCREEN_OFF = 1 << 4;
+    private static final int WAKE_LOCK_STAY_AWAKE = 1 << 5; // only set if already awake
 
     // Summarizes the user activity state.
     private static final int USER_ACTIVITY_SCREEN_BRIGHT = 1 << 0;
@@ -152,6 +156,12 @@
     // See point of use for more details.
     private static final int WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT = 95;
 
+    // The name of the boot animation service in init.rc.
+    private static final String BOOT_ANIMATION_SERVICE = "bootanim";
+
+    // Poll interval in milliseconds for watching boot animation finished.
+    private static final int BOOT_ANIMATION_POLL_INTERVAL = 200;
+
     private Context mContext;
     private LightsService mLightsService;
     private BatteryService mBatteryService;
@@ -1172,16 +1182,25 @@
                         if (mWakefulness != WAKEFULNESS_ASLEEP) {
                             mWakeLockSummary |= WAKE_LOCK_CPU
                                     | WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT;
+                            if (mWakefulness == WAKEFULNESS_AWAKE) {
+                                mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE;
+                            }
                         }
                         break;
                     case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
                         if (mWakefulness != WAKEFULNESS_ASLEEP) {
                             mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_BRIGHT;
+                            if (mWakefulness == WAKEFULNESS_AWAKE) {
+                                mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE;
+                            }
                         }
                         break;
                     case PowerManager.SCREEN_DIM_WAKE_LOCK:
                         if (mWakefulness != WAKEFULNESS_ASLEEP) {
                             mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_DIM;
+                            if (mWakefulness == WAKEFULNESS_AWAKE) {
+                                mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE;
+                            }
                         }
                         break;
                     case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
@@ -1339,7 +1358,7 @@
     private boolean isBeingKeptAwakeLocked() {
         return mStayOn
                 || mProximityPositive
-                || (mWakeLockSummary & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) != 0
+                || (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0
                 || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
                         | USER_ACTIVITY_SCREEN_DIM)) != 0;
     }
@@ -1652,6 +1671,29 @@
         updatePowerStateLocked();
     }
 
+    private void startWatchingForBootAnimationFinished() {
+        mHandler.sendEmptyMessage(MSG_CHECK_IF_BOOT_ANIMATION_FINISHED);
+    }
+
+    private void checkIfBootAnimationFinished() {
+        if (DEBUG) {
+            Slog.d(TAG, "Check if boot animation finished...");
+        }
+
+        if (SystemService.isRunning(BOOT_ANIMATION_SERVICE)) {
+            mHandler.sendEmptyMessageDelayed(MSG_CHECK_IF_BOOT_ANIMATION_FINISHED,
+                    BOOT_ANIMATION_POLL_INTERVAL);
+            return;
+        }
+
+        synchronized (mLock) {
+            if (!mBootCompleted) {
+                Slog.i(TAG, "Boot animation finished.");
+                handleBootCompletedLocked();
+            }
+        }
+    }
+
     private void handleBootCompletedLocked() {
         final long now = SystemClock.uptimeMillis();
         mBootCompleted = true;
@@ -2160,9 +2202,13 @@
     private final class BootCompletedReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-            synchronized (mLock) {
-                handleBootCompletedLocked();
-            }
+            // This is our early signal that the system thinks it has finished booting.
+            // However, the boot animation may still be running for a few more seconds
+            // since it is ultimately in charge of when it terminates.
+            // Defer transitioning into the boot completed state until the animation exits.
+            // We do this so that the screen does not start to dim prematurely before
+            // the user has actually had a chance to interact with the device.
+            startWatchingForBootAnimationFinished();
         }
     }
 
@@ -2217,6 +2263,9 @@
                 case MSG_SCREEN_ON_BLOCKER_RELEASED:
                     handleScreenOnBlockerReleased();
                     break;
+                case MSG_CHECK_IF_BOOT_ANIMATION_FINISHED:
+                    checkIfBootAnimationFinished();
+                    break;
             }
         }
     }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index f1123aa..abbeb82 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -2752,10 +2752,7 @@
                 }
             }
 
-            if (DEBUG_LAYOUT
-                    // TODO: Remove once b/7094175 is fixed
-                    || ((String)win.mAttrs.getTitle()).contains("Keyguard")
-                ) Slog.v(TAG, "Relayout " + win + ": viewVisibility=" + viewVisibility
+            if (DEBUG_LAYOUT) Slog.v(TAG, "Relayout " + win + ": viewVisibility=" + viewVisibility
                     + " " + requestedWidth + "x" + requestedHeight + " " + win.mAttrs);
 
             win.mEnforceSizeCompat = (win.mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0;
@@ -9275,9 +9272,7 @@
                         "Reporting new frame to " + win + ": " + win.mCompatFrame);
                 int diff = 0;
                 boolean configChanged = win.isConfigChanged();
-                if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION
-                        // TODO: Remove once b/7094175 is fixed
-                        || ((String)win.mAttrs.getTitle()).contains("Keyguard"))
+                if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION)
                         && configChanged) {
                     Slog.i(TAG, "Sending new config to window " + win + ": "
                             + winAnimator.mSurfaceW + "x" + winAnimator.mSurfaceH
diff --git a/services/jni/com_android_server_power_PowerManagerService.cpp b/services/jni/com_android_server_power_PowerManagerService.cpp
index 75f77b9..23c33af 100644
--- a/services/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/jni/com_android_server_power_PowerManagerService.cpp
@@ -168,12 +168,14 @@
 }
 
 static void nativeSetInteractive(JNIEnv *env, jclass clazz, jboolean enable) {
-    if (enable) {
-        ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(true) while turning screen on");
-        gPowerModule->setInteractive(gPowerModule, true);
-    } else {
-        ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(false) while turning screen off");
-        gPowerModule->setInteractive(gPowerModule, false);
+    if (gPowerModule) {
+        if (enable) {
+            ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(true) while turning screen on");
+            gPowerModule->setInteractive(gPowerModule, true);
+        } else {
+            ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(false) while turning screen off");
+            gPowerModule->setInteractive(gPowerModule, false);
+        }
     }
 }
 
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index 4440145..9c727f9 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -98,6 +98,8 @@
     static final int POOR_LINK_DETECTED                             = BASE + 21;
     static final int GOOD_LINK_DETECTED                             = BASE + 22;
 
+    public static final boolean DEFAULT_POOR_NETWORK_AVOIDANCE_ENABLED = false;
+
     /*
      * RSSI levels as used by notification icon
      * Level 4  -55 <= RSSI
@@ -345,13 +347,6 @@
         // watchdog in an enabled state
         putSettingsGlobalBoolean(contentResolver, Settings.Global.WIFI_WATCHDOG_ON, true);
 
-        // disable poor network avoidance
-        if (sWifiOnly) {
-            logd("Disabling poor network avoidance for wi-fi only device");
-            putSettingsGlobalBoolean(contentResolver,
-                    Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, false);
-        }
-
         WifiWatchdogStateMachine wwsm = new WifiWatchdogStateMachine(context);
         wwsm.start();
         return wwsm;
@@ -441,8 +436,15 @@
     private void updateSettings() {
         if (DBG) logd("Updating secure settings");
 
-        mPoorNetworkDetectionEnabled = getSettingsGlobalBoolean(mContentResolver,
-                Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, true);
+        // disable poor network avoidance
+        if (sWifiOnly) {
+            logd("Disabling poor network avoidance for wi-fi only device");
+            mPoorNetworkDetectionEnabled = false;
+        } else {
+            mPoorNetworkDetectionEnabled = getSettingsGlobalBoolean(mContentResolver,
+                    Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED,
+                    DEFAULT_POOR_NETWORK_AVOIDANCE_ENABLED);
+        }
     }
 
     /**