Merge "Fix bug #7326778 Rename LayoutParams.onResolveLayoutDirection(int)" into jb-mr1-dev
diff --git a/api/17.txt b/api/17.txt
index 9d35348..7cadf9b 100644
--- a/api/17.txt
+++ b/api/17.txt
@@ -18905,7 +18905,7 @@
     field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
     field public static final java.lang.String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
     field public static final java.lang.String LOCK_PATTERN_ENABLED = "lock_pattern_autolock";
-    field public static final java.lang.String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
+    field public static final deprecated java.lang.String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
     field public static final java.lang.String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern";
     field public static final deprecated java.lang.String LOGGING_ID = "logging_id";
     field public static final deprecated java.lang.String NETWORK_PREFERENCE = "network_preference";
diff --git a/api/current.txt b/api/current.txt
index 9d35348..7cadf9b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18905,7 +18905,7 @@
     field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
     field public static final java.lang.String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
     field public static final java.lang.String LOCK_PATTERN_ENABLED = "lock_pattern_autolock";
-    field public static final java.lang.String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
+    field public static final deprecated java.lang.String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
     field public static final java.lang.String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern";
     field public static final deprecated java.lang.String LOGGING_ID = "logging_id";
     field public static final deprecated java.lang.String NETWORK_PREFERENCE = "network_preference";
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 fc569e0..5cde65c 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,10 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.RegisteredServicesCache;
 import android.content.pm.RegisteredServicesCacheListener;
 import android.content.pm.UserInfo;
-import android.content.res.Resources;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
 import android.database.sqlite.SQLiteDatabase;
@@ -55,10 +55,13 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
+import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.R;
 import com.android.internal.util.IndentingPrintWriter;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Sets;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -67,6 +70,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -243,8 +247,7 @@
     }
 
     public void systemReady() {
-        mAuthenticatorCache.generateServicesMap();
-        initUser(0);
+        initUser(UserHandle.USER_OWNER);
     }
 
     private UserManager getUserManager() {
@@ -261,7 +264,7 @@
                 accounts = new UserAccounts(mContext, userId);
                 mUsers.append(userId, accounts);
                 purgeOldGrants(accounts);
-                validateAccountsAndPopulateCache(accounts);
+                validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
             }
             return accounts;
         }
@@ -299,7 +302,34 @@
         }
     }
 
-    private void validateAccountsAndPopulateCache(UserAccounts accounts) {
+    /**
+     * Validate internal set of accounts against installed authenticators for
+     * given user. Clears cached authenticators before validating.
+     */
+    public void validateAccounts(int userId) {
+        final UserAccounts accounts = getUserAccounts(userId);
+
+        // Invalidate user-specific cache to make sure we catch any
+        // removed authenticators.
+        validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
+    }
+
+    /**
+     * Validate internal set of accounts against installed authenticators for
+     * given user. Clear cached authenticators before validating when requested.
+     */
+    private void validateAccountsInternal(
+            UserAccounts accounts, boolean invalidateAuthenticatorCache) {
+        if (invalidateAuthenticatorCache) {
+            mAuthenticatorCache.invalidateCache(accounts.userId);
+        }
+
+        final HashSet<AuthenticatorDescription> knownAuth = Sets.newHashSet();
+        for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service :
+                mAuthenticatorCache.getAllServices(accounts.userId)) {
+            knownAuth.add(service.type);
+        }
+
         synchronized (accounts.cacheLock) {
             final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
             boolean accountDeleted = false;
@@ -314,9 +344,9 @@
                     final long accountId = cursor.getLong(0);
                     final String accountType = cursor.getString(1);
                     final String accountName = cursor.getString(2);
-                    if (mAuthenticatorCache.getServiceInfo(
-                            AuthenticatorDescription.newKey(accountType)) == null) {
-                        Log.d(TAG, "deleting account " + accountName + " because type "
+
+                    if (!knownAuth.contains(AuthenticatorDescription.newKey(accountType))) {
+                        Slog.w(TAG, "deleting account " + accountName + " because type "
                                 + accountType + " no longer has a registered authenticator");
                         db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
                         accountDeleted = true;
@@ -390,20 +420,10 @@
         }
     }
 
-    private List<UserInfo> getAllUsers() {
-        return getUserManager().getUsers();
-    }
-
-    public void onServiceChanged(AuthenticatorDescription desc, boolean removed) {
-        // Validate accounts for all users
-        List<UserInfo> users = getAllUsers();
-        if (users == null) {
-            validateAccountsAndPopulateCache(getUserAccountsForCaller());
-        } else {
-            for (UserInfo user : users) {
-                validateAccountsAndPopulateCache(getUserAccounts(user.id));
-            }
-        }
+    @Override
+    public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
+        Slog.d(TAG, "onServiceChanged() for userId " + userId);
+        validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
     }
 
     public String getPassword(Account account) {
@@ -470,10 +490,11 @@
                     + "caller's uid " + Binder.getCallingUid()
                     + ", pid " + Binder.getCallingPid());
         }
-        long identityToken = clearCallingIdentity();
+        final int userId = UserHandle.getCallingUserId();
+        final long identityToken = clearCallingIdentity();
         try {
             Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
-                    authenticatorCollection = mAuthenticatorCache.getAllServices();
+                    authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
             AuthenticatorDescription[] types =
                     new AuthenticatorDescription[authenticatorCollection.size()];
             int i = 0;
@@ -1055,9 +1076,9 @@
         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
         checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
         final UserAccounts accounts = getUserAccountsForCaller();
-        AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
-            mAuthenticatorCache.getServiceInfo(
-                    AuthenticatorDescription.newKey(account.type));
+        final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
+        authenticatorInfo = mAuthenticatorCache.getServiceInfo(
+                AuthenticatorDescription.newKey(account.type), accounts.userId);
         final boolean customTokens =
             authenticatorInfo != null && authenticatorInfo.type.customTokens;
 
@@ -1074,7 +1095,7 @@
         if (notifyOnAuthFailure) {
             loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
         }
-        
+
         long identityToken = clearCallingIdentity();
         try {
             // if the caller has permission, do the peek. otherwise go the more expensive
@@ -1183,28 +1204,6 @@
                 account, authTokenType, uid), n, user);
     }
 
-    String getAccountLabel(String accountType) {
-        RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo =
-            mAuthenticatorCache.getServiceInfo(
-                    AuthenticatorDescription.newKey(accountType));
-        if (serviceInfo == null) {
-            throw new IllegalArgumentException("unknown account type: " + accountType);
-        }
-
-        final Context authContext;
-        try {
-            authContext = mContext.createPackageContext(
-                    serviceInfo.type.packageName, 0);
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new IllegalArgumentException("unknown account type: " + accountType);
-        }
-        try {
-            return authContext.getString(serviceInfo.type.labelId);
-        } catch (Resources.NotFoundException e) {
-            throw new IllegalArgumentException("unknown account type: " + accountType);
-        }
-    }
-
     private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
             AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) {
 
@@ -1506,28 +1505,48 @@
     }
 
     /**
-     * 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);
+        }
+        return getAccounts(runningUserIds);
+    }
 
-        synchronized(mUsers) {
-            for (UserInfo user : users) {
-                UserAccounts userAccounts = getUserAccounts(user.id);
+    /** {@hide} */
+    public AccountAndUser[] getAllAccounts() {
+        final List<UserInfo> users = getUserManager().getUsers();
+        final int[] userIds = new int[users.size()];
+        for (int i = 0; i < userIds.length; i++) {
+            userIds[i] = users.get(i).id;
+        }
+        return getAccounts(userIds);
+    }
+
+    private AccountAndUser[] getAccounts(int[] userIds) {
+        final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
+        synchronized (mUsers) {
+            for (int userId : userIds) {
+                UserAccounts userAccounts = getUserAccounts(userId);
                 if (userAccounts == null) continue;
                 synchronized (userAccounts.cacheLock) {
                     Account[] accounts = getAccountsFromCacheLocked(userAccounts, null);
                     for (int a = 0; a < accounts.length; a++) {
-                        allAccounts.add(new AccountAndUser(accounts[a], user.id));
+                        runningAccounts.add(new AccountAndUser(accounts[a], userId));
                     }
                 }
             }
         }
-        AccountAndUser[] accountsArray = new AccountAndUser[allAccounts.size()];
-        return allAccounts.toArray(accountsArray);
+
+        AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
+        return runningAccounts.toArray(accountsArray);
     }
 
     public Account[] getAccounts(String type) {
@@ -1836,9 +1855,9 @@
          * if no authenticator or the bind fails then return false, otherwise return true
          */
         private boolean bindToAuthenticator(String authenticatorType) {
-            AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
-                    mAuthenticatorCache.getServiceInfo(
-                            AuthenticatorDescription.newKey(authenticatorType));
+            final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
+            authenticatorInfo = mAuthenticatorCache.getServiceInfo(
+                    AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
             if (authenticatorInfo == null) {
                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
                     Log.v(TAG, "there is no authenticator for " + authenticatorType
@@ -2024,6 +2043,7 @@
         return false;
     }
 
+    @Override
     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -2033,17 +2053,15 @@
             return;
         }
         final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
+        final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, "  ");
 
-        fout = new IndentingPrintWriter(fout, "  ");
-        int size = mUsers.size();
-        for (int i = 0; i < size; i++) {
-            fout.println("User " + mUsers.keyAt(i) + ":");
-            ((IndentingPrintWriter) fout).increaseIndent();
-            dumpUser(mUsers.valueAt(i), fd, fout, args, isCheckinRequest);
-            ((IndentingPrintWriter) fout).decreaseIndent();
-            if (i < size - 1) {
-                fout.println();
-            }
+        final List<UserInfo> users = getUserManager().getUsers();
+        for (UserInfo user : users) {
+            ipw.println("User " + user + ":");
+            ipw.increaseIndent();
+            dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
+            ipw.println();
+            ipw.decreaseIndent();
         }
     }
 
@@ -2083,7 +2101,7 @@
                 }
 
                 fout.println();
-                mAuthenticatorCache.dump(fd, fout, args);
+                mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
             }
         }
     }
@@ -2154,11 +2172,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 +2214,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/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/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..93c9526 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,42 @@
         }
     }
 
-    private void onUserRemoved(Intent intent) {
-        int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
-        if (userId == -1) return;
-        removeUser(userId);
+    private void onUserStarting(int userId) {
+        // Make sure that accounts we're about to use are valid
+        AccountManagerService.getSingleton().validateAccounts(userId);
+
+        mSyncAdapters.invalidateCache(userId);
+
+        updateRunningAccounts();
+
+        final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId);
+        mSyncStorageEngine.doDatabaseCleanup(accounts, userId);
+
+        mSyncQueue.addPendingOperations(userId);
+
+        // Schedule sync for any accounts under started user
+        for (Account account : accounts) {
+            scheduleSync(account, userId, null, null, 0 /* no delay */,
+                    true /* onlyThoseWithUnknownSyncableState */);
+        }
+
+        sendCheckAlarmsMessage();
     }
 
-    private void 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 +1056,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,13 +1075,13 @@
         if (users != null) {
             for (UserInfo user : users) {
                 pw.print("u" + user.id + "="
-                        + mSyncStorageEngine.getMasterSyncAutomatically(user.id));
+                        + mSyncStorageEngine.getMasterSyncAutomatically(user.id) + " ");
             }
             pw.println();
         }
         pw.print("memory low: "); pw.println(mStorageIsLow);
 
-        final AccountAndUser[] accounts = mAccounts;
+        final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts();
 
         pw.print("accounts: ");
         if (accounts != INITIAL_ACCOUNTS_ARRAY) {
@@ -1153,7 +1143,7 @@
                     pw.print(" "); pw.print(account.account.type);
                     pw.println(":");
             for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType :
-                    mSyncAdapters.getAllServices()) {
+                    mSyncAdapters.getAllServices(account.userId)) {
                 if (!syncAdapterType.type.accountType.equals(account.account.type)) {
                     continue;
                 }
@@ -1530,6 +1520,23 @@
         }
     }
 
+    private void dumpSyncAdapters(IndentingPrintWriter pw) {
+        pw.println();
+        final List<UserInfo> users = getAllUsers();
+        if (users != null) {
+            for (UserInfo user : users) {
+                pw.println("Sync adapters for " + user + ":");
+                pw.increaseIndent();
+                for (RegisteredServicesCache.ServiceInfo<?> info :
+                        mSyncAdapters.getAllServices(user.id)) {
+                    pw.println(info);
+                }
+                pw.decreaseIndent();
+                pw.println();
+            }
+        }
+    }
+
     private static class AuthoritySyncStats {
         String name;
         long elapsedTime;
@@ -1613,18 +1620,10 @@
                 Maps.newHashMap();
 
         private volatile CountDownLatch mReadyToRunLatch = new CountDownLatch(1);
+
         public void onBootCompleted() {
             mBootCompleted = true;
-            // TODO: Handle bootcompleted event for specific user. Now let's just iterate through
-            // all the users.
-            List<UserInfo> users = getAllUsers();
-            if (users != null) {
-                for (UserInfo user : users) {
-                    mSyncStorageEngine.doDatabaseCleanup(
-                            AccountManagerService.getSingleton().getAccounts(user.id),
-                            user.id);
-                }
-            }
+
             if (mReadyToRunLatch != null) {
                 mReadyToRunLatch.countDown();
             }
@@ -1814,7 +1813,7 @@
                 return earliestFuturePollTime;
             }
 
-            AccountAndUser[] accounts = mAccounts;
+            AccountAndUser[] accounts = mRunningAccounts;
 
             final long nowAbsolute = System.currentTimeMillis();
             final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis)
@@ -1869,9 +1868,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 +1927,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 +1998,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 +2030,7 @@
                 for (Integer user : removedUsers) {
                     // if it's still removed
                     if (mUserManager.getUserInfo(user) == null) {
-                        removeUser(user);
+                        onUserRemoved(user);
                     }
                 }
             }
@@ -2167,8 +2167,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/provider/Settings.java b/core/java/android/provider/Settings.java
index 3c4a8fe..00ea873 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3197,10 +3197,16 @@
         public static final String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern";
 
         /**
-         * Whether lock pattern will vibrate as user enters (0 = false, 1 = true)
+         * Whether lock pattern will vibrate as user enters (0 = false, 1 =
+         * true)
+         *
+         * @deprecated Starting in {@link VERSION_CODES#JELLY_BEAN_MR1} the
+         *             lockscreen uses
+         *             {@link Settings.System#HAPTIC_FEEDBACK_ENABLED}.
          */
-        public static final String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED =
-            "lock_pattern_tactile_feedback_enabled";
+        @Deprecated
+        public static final String
+                LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
 
         /**
          * This preference allows the device to be locked given time after screen goes off,
diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java
index a51a8f6..d6576e1 100644
--- a/core/java/android/webkit/AccessibilityInjector.java
+++ b/core/java/android/webkit/AccessibilityInjector.java
@@ -21,6 +21,10 @@
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.speech.tts.TextToSpeech;
+import android.speech.tts.TextToSpeech.Engine;
+import android.speech.tts.TextToSpeech.OnInitListener;
+import android.speech.tts.UtteranceProgressListener;
+import android.util.Log;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.accessibility.AccessibilityManager;
@@ -44,6 +48,10 @@
  * APIs.
  */
 class AccessibilityInjector {
+    private static final String TAG = AccessibilityInjector.class.getSimpleName();
+
+    private static boolean DEBUG = false;
+
     // The WebViewClassic this injector is responsible for managing.
     private final WebViewClassic mWebViewClassic;
 
@@ -90,6 +98,10 @@
     private static final String ACCESSIBILITY_ANDROIDVOX_TEMPLATE =
             "cvox.AndroidVox.performAction('%1s')";
 
+    // JS code used to shut down an active AndroidVox instance.
+    private static final String TOGGLE_CVOX_TEMPLATE =
+            "javascript:(function() { cvox.ChromeVox.host.activateOrDeactivateChromeVox(%b); })();";
+
     /**
      * Creates an instance of the AccessibilityInjector based on
      * {@code webViewClassic}.
@@ -117,6 +129,7 @@
 
         addTtsApis();
         addCallbackApis();
+        toggleAndroidVox(true);
     }
 
     /**
@@ -126,10 +139,20 @@
      * </p>
      */
     public void removeAccessibilityApisIfNecessary() {
+        toggleAndroidVox(false);
         removeTtsApis();
         removeCallbackApis();
     }
 
+    private void toggleAndroidVox(boolean state) {
+        if (!mAccessibilityScriptInjected) {
+            return;
+        }
+
+        final String code = String.format(TOGGLE_CVOX_TEMPLATE, state);
+        mWebView.loadUrl(code);
+    }
+
     /**
      * Initializes an {@link AccessibilityNodeInfo} with the actions and
      * movement granularity levels supported by this
@@ -196,7 +219,7 @@
         if (mAccessibilityScriptInjected) {
             return sendActionToAndroidVox(action, arguments);
         }
-        
+
         if (mAccessibilityInjectorFallback != null) {
             return mAccessibilityInjectorFallback.performAccessibilityAction(action, arguments);
         }
@@ -262,6 +285,9 @@
      */
     public void onPageStarted(String url) {
         mAccessibilityScriptInjected = false;
+        if (DEBUG)
+            Log.w(TAG, "[" + mWebView.hashCode() + "] Started loading new page");
+        addAccessibilityApisIfNecessary();
     }
 
     /**
@@ -282,15 +308,23 @@
         if (!shouldInjectJavaScript(url)) {
             mAccessibilityScriptInjected = false;
             toggleFallbackAccessibilityInjector(true);
+            if (DEBUG)
+                Log.d(TAG, "[" + mWebView.hashCode() + "] Using fallback accessibility support");
             return;
         }
 
         toggleFallbackAccessibilityInjector(false);
 
-        final String injectionUrl = getScreenReaderInjectionUrl();
-        mWebView.loadUrl(injectionUrl);
-
-        mAccessibilityScriptInjected = true;
+        if (!mAccessibilityScriptInjected) {
+            mAccessibilityScriptInjected = true;
+            final String injectionUrl = getScreenReaderInjectionUrl();
+            mWebView.loadUrl(injectionUrl);
+            if (DEBUG)
+                Log.d(TAG, "[" + mWebView.hashCode() + "] Loading screen reader into WebView");
+        } else {
+            if (DEBUG)
+                Log.w(TAG, "[" + mWebView.hashCode() + "] Attempted to inject screen reader twice");
+        }
     }
 
     /**
@@ -368,6 +402,8 @@
         if (mTextToSpeech != null) {
             return;
         }
+        if (DEBUG)
+            Log.d(TAG, "[" + mWebView.hashCode() + "] Adding TTS APIs into WebView");
         mTextToSpeech = new TextToSpeechWrapper(mContext);
         mWebView.addJavascriptInterface(mTextToSpeech, ALIAS_TTS_JS_INTERFACE);
     }
@@ -381,6 +417,8 @@
             return;
         }
 
+        if (DEBUG)
+            Log.d(TAG, "[" + mWebView.hashCode() + "] Removing TTS APIs from WebView");
         mWebView.removeJavascriptInterface(ALIAS_TTS_JS_INTERFACE);
         mTextToSpeech.stop();
         mTextToSpeech.shutdown();
@@ -527,35 +565,141 @@
      * Used to protect the TextToSpeech class, only exposing the methods we want to expose.
      */
     private static class TextToSpeechWrapper {
-        private TextToSpeech mTextToSpeech;
+        private static final String WRAP_TAG = TextToSpeechWrapper.class.getSimpleName();
+
+        private final HashMap<String, String> mTtsParams;
+        private final TextToSpeech mTextToSpeech;
+
+        /**
+         * Whether this wrapper is ready to speak. If this is {@code true} then
+         * {@link #mShutdown} is guaranteed to be {@code false}.
+         */
+        private volatile boolean mReady;
+
+        /**
+         * Whether this wrapper was shut down. If this is {@code true} then
+         * {@link #mReady} is guaranteed to be {@code false}.
+         */
+        private volatile boolean mShutdown;
 
         public TextToSpeechWrapper(Context context) {
+            if (DEBUG)
+                Log.d(WRAP_TAG, "[" + hashCode() + "] Initializing text-to-speech on thread "
+                        + Thread.currentThread().getId() + "...");
+
             final String pkgName = context.getPackageName();
-            mTextToSpeech = new TextToSpeech(context, null, null, pkgName + ".**webview**", true);
+
+            mReady = false;
+            mShutdown = false;
+
+            mTtsParams = new HashMap<String, String>();
+            mTtsParams.put(Engine.KEY_PARAM_UTTERANCE_ID, WRAP_TAG);
+
+            mTextToSpeech = new TextToSpeech(
+                    context, mInitListener, null, pkgName + ".**webview**", true);
+            mTextToSpeech.setOnUtteranceProgressListener(mErrorListener);
         }
 
         @JavascriptInterface
         @SuppressWarnings("unused")
         public boolean isSpeaking() {
-            return mTextToSpeech.isSpeaking();
+            synchronized (mTextToSpeech) {
+                if (!mReady) {
+                    return false;
+                }
+
+                return mTextToSpeech.isSpeaking();
+            }
         }
 
         @JavascriptInterface
         @SuppressWarnings("unused")
         public int speak(String text, int queueMode, HashMap<String, String> params) {
-            return mTextToSpeech.speak(text, queueMode, params);
+            synchronized (mTextToSpeech) {
+                if (!mReady) {
+                    if (DEBUG)
+                        Log.w(WRAP_TAG, "[" + hashCode() + "] Attempted to speak before TTS init");
+                    return TextToSpeech.ERROR;
+                } else {
+                    if (DEBUG)
+                        Log.i(WRAP_TAG, "[" + hashCode() + "] Speak called from JS binder");
+                }
+
+                return mTextToSpeech.speak(text, queueMode, params);
+            }
         }
 
         @JavascriptInterface
         @SuppressWarnings("unused")
         public int stop() {
-            return mTextToSpeech.stop();
+            synchronized (mTextToSpeech) {
+                if (!mReady) {
+                    if (DEBUG)
+                        Log.w(WRAP_TAG, "[" + hashCode() + "] Attempted to stop before initialize");
+                    return TextToSpeech.ERROR;
+                } else {
+                    if (DEBUG)
+                        Log.i(WRAP_TAG, "[" + hashCode() + "] Stop called from JS binder");
+                }
+
+                return mTextToSpeech.stop();
+            }
         }
 
         @SuppressWarnings("unused")
         protected void shutdown() {
-            mTextToSpeech.shutdown();
+            synchronized (mTextToSpeech) {
+                if (!mReady) {
+                    if (DEBUG)
+                        Log.w(WRAP_TAG, "[" + hashCode() + "] Called shutdown before initialize");
+                } else {
+                    if (DEBUG)
+                        Log.i(WRAP_TAG, "[" + hashCode() + "] Shutting down text-to-speech from "
+                                + "thread " + Thread.currentThread().getId() + "...");
+                }
+                mShutdown = true;
+                mReady = false;
+                mTextToSpeech.shutdown();
+            }
         }
+
+        private final OnInitListener mInitListener = new OnInitListener() {
+            @Override
+            public void onInit(int status) {
+                synchronized (mTextToSpeech) {
+                    if (!mShutdown && (status == TextToSpeech.SUCCESS)) {
+                        if (DEBUG)
+                            Log.d(WRAP_TAG, "[" + TextToSpeechWrapper.this.hashCode()
+                                    + "] Initialized successfully");
+                        mReady = true;
+                    } else {
+                        if (DEBUG)
+                            Log.w(WRAP_TAG, "[" + TextToSpeechWrapper.this.hashCode()
+                                    + "] Failed to initialize");
+                        mReady = false;
+                    }
+                }
+            }
+        };
+
+        private final UtteranceProgressListener mErrorListener = new UtteranceProgressListener() {
+            @Override
+            public void onStart(String utteranceId) {
+                // Do nothing.
+            }
+
+            @Override
+            public void onError(String utteranceId) {
+                if (DEBUG)
+                    Log.w(WRAP_TAG, "[" + TextToSpeechWrapper.this.hashCode()
+                            + "] Failed to speak utterance");
+            }
+
+            @Override
+            public void onDone(String utteranceId) {
+                // Do nothing.
+            }
+        };
     }
 
     /**
@@ -625,6 +769,8 @@
          * @return Whether the result was received.
          */
         private boolean waitForResultTimedLocked(int resultId) {
+            if (DEBUG)
+                Log.d(TAG, "Waiting for CVOX result...");
             long waitTimeMillis = RESULT_TIMEOUT;
             final long startTimeMillis = SystemClock.uptimeMillis();
             while (true) {
@@ -642,6 +788,8 @@
                     }
                     mResultLock.wait(waitTimeMillis);
                 } catch (InterruptedException ie) {
+                    if (DEBUG)
+                        Log.w(TAG, "Timed out while waiting for CVOX result");
                     /* ignore */
                 }
             }
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index b6f0862..2f31ebd 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -648,6 +648,8 @@
 
         int largestChildHeight = Integer.MIN_VALUE;
 
+        final int layoutDirection = getLayoutDirection();
+
         // See how tall everyone is. Also remember max width.
         for (int i = 0; i < count; ++i) {
             final View child = getVirtualChildAt(i);
@@ -667,6 +669,7 @@
             }
 
             LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
+            lp.onResolveLayoutDirection(layoutDirection);
 
             totalWeight += lp.weight;
             
@@ -989,6 +992,8 @@
 
         int largestChildWidth = Integer.MIN_VALUE;
 
+        final int layoutDirection = getLayoutDirection();
+
         // See how wide everyone is. Also remember max height.
         for (int i = 0; i < count; ++i) {
             final View child = getVirtualChildAt(i);
@@ -1009,6 +1014,7 @@
 
             final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
                     child.getLayoutParams();
+            lp.onResolveLayoutDirection(layoutDirection);
 
             totalWeight += lp.weight;
             
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/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index d14b1ee..3f40f20 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -949,14 +949,8 @@
      * @return Whether tactile feedback for the pattern is enabled.
      */
     public boolean isTactileFeedbackEnabled() {
-        return getBoolean(Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, false);
-    }
-
-    /**
-     * Set whether tactile feedback for the pattern is enabled.
-     */
-    public void setTactileFeedbackEnabled(boolean enabled) {
-        setBoolean(Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, enabled);
+        return Settings.System.getIntForUser(mContentResolver,
+                Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
     }
 
     /**
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 56db116..631cdae 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -75,6 +75,14 @@
     return state;
 }
 
+static jboolean
+android_media_AudioSystem_isSourceActive(JNIEnv *env, jobject thiz, jint source)
+{
+    bool state = false;
+    AudioSystem::isSourceActive((audio_source_t) source, &state);
+    return state;
+}
+
 static int
 android_media_AudioSystem_setParameters(JNIEnv *env, jobject thiz, jstring keyValuePairs)
 {
@@ -261,7 +269,8 @@
     {"getParameters",        "(Ljava/lang/String;)Ljava/lang/String;", (void *)android_media_AudioSystem_getParameters},
     {"muteMicrophone",      "(Z)I",     (void *)android_media_AudioSystem_muteMicrophone},
     {"isMicrophoneMuted",   "()Z",      (void *)android_media_AudioSystem_isMicrophoneMuted},
-    {"isStreamActive",      "(II)Z",     (void *)android_media_AudioSystem_isStreamActive},
+    {"isStreamActive",      "(II)Z",    (void *)android_media_AudioSystem_isStreamActive},
+    {"isSourceActive",      "(I)Z",     (void *)android_media_AudioSystem_isSourceActive},
     {"setDeviceConnectionState", "(IILjava/lang/String;)I", (void *)android_media_AudioSystem_setDeviceConnectionState},
     {"getDeviceConnectionState", "(ILjava/lang/String;)I",  (void *)android_media_AudioSystem_getDeviceConnectionState},
     {"setPhoneState",       "(I)I",     (void *)android_media_AudioSystem_setPhoneState},
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 26d04e9..4698002 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -988,4 +988,9 @@
     <!-- Whether camera shutter sound is forced or not  (country specific). -->
     <bool name="config_camera_sound_forced">false</bool>
 
+    <!-- Set to true if we need to not prefer an APN.
+         This is being added to enable a simple scenario of pre-paid
+         provisioning on some carriers, working around a bug (7305641)
+         where if the preferred is used we don't try the others. -->
+    <bool name="config_dontPreferApn">false</bool>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1c71e64..72de22c 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3936,7 +3936,7 @@
     </string>
 
     <!-- Text spoken when the user is performing a gesture that will enable accessibility. [CHAR LIMIT=none] -->
-    <string name="continue_to_enable_accessibility">Keep holding down your two fingers to enable accessibility.</string>
+    <string name="continue_to_enable_accessibility">Keep holding down two fingers to enable accessibility.</string>
     <!-- Text spoken when the user enabled accessibility. [CHAR LIMIT=none] -->
     <string name="accessibility_enabled">Accessibility enabled.</string>
     <!-- Text spoken when the user stops preforming a gesture that would enable accessibility. [CHAR LIMIT=none] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fae63db..c48de1f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -276,6 +276,7 @@
   <java-symbol type="bool" name="config_useDevInputEventForAudioJack" />
   <java-symbol type="bool" name="config_safe_media_volume_enabled" />
   <java-symbol type="bool" name="config_camera_sound_forced" />
+  <java-symbol type="bool" name="config_dontPreferApn" />
 
   <java-symbol type="integer" name="config_cursorWindowSize" />
   <java-symbol type="integer" name="config_longPressOnPowerBehavior" />
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/docs/html/guide/google/gcm/adv.jd b/docs/html/guide/google/gcm/adv.jd
index aa66e25..356ee1d 100644
--- a/docs/html/guide/google/gcm/adv.jd
+++ b/docs/html/guide/google/gcm/adv.jd
@@ -163,7 +163,7 @@
 <p>There are two ways to unregister a device from GCM: manually and automatically.</p>
 <p>An Android application can manually unregister itself by issuing a <code>com.google.android.c2dm.intent.UNREGISTER</code> intent, which is useful when the application offers a logoff feature (so it can unregister on logoff and register again on logon). See the <a href="gcm.html#unregistering">Architectural Overview</a> for more discussion of this topic. This is the sequence of events when an application unregisters itself:</p>
 <ol>
-  <li> The application issues a <code>com.google.android.c2dm.intent.UNREGISTER</code> intent, passing the registration ID (the application should have saved its registration ID when it received the proper <code>com.google.android.c2dm.intent.REGISTRATION</code> intent).</li>
+  <li> The application issues a <code>com.google.android.c2dm.intent.UNREGISTER</code> intent, passing the package name as an extra.</li>
   <li>When the GCM server is done with the unregistration, it sends a <code>com.google.android.c2dm.intent.REGISTRATION</code> intent with the <code>unregistered</code> extra set.</li>
   <li>The application then must contact the 3rd-party server so it can remove the registration ID.</li>
   <li>The application should also clear its registration ID.
@@ -174,7 +174,7 @@
   <li>The end user uninstalls the application.</li>
   <li>The 3rd-party server sends a message to GCM server.</li>
   <li>The GCM server sends the message to the device.</li>
-  <li>The GCM client receives the message and queries Package Manager about whether there are broadcast receivers configured to receive it, which returns <code>false</code>.
+  <li>The GCM client receives the message and queries Package Manager about whether there are broadcast receivers configured to receive it, which returns <code>false</code>. 
 </li>
   <li>The GCM client informs the GCM server that the application was uninstalled.</li>
   <li>The GCM server marks the registration ID for deletion.</li>
@@ -183,6 +183,9 @@
   <li>The 3rd-party deletes the registration ID.
   </li>
 </ol>
+
+<p class ="note"><strong>Note:</strong> The GCM client is the Google Cloud Messaging framework present on the device.</p>
+
 <p>Note that it might take a while for the registration ID be completely removed from GCM. Thus it is possible that messages sent during step 7 above gets a valid message ID as response, even though the message will not be delivered to the device. Eventually, the registration ID will be removed and the server will get a <code>NotRegistered</code> error, without any further action being required from the 3rd-party server (this scenario happens frequently while an application is being developed and tested).</p>
 
 <h2 id="collapsible">Send-to-Sync  vs. Messages with Payload</h2>
diff --git a/docs/html/guide/google/gcm/gcm.jd b/docs/html/guide/google/gcm/gcm.jd
index c4dfecf..a47ceb9 100644
--- a/docs/html/guide/google/gcm/gcm.jd
+++ b/docs/html/guide/google/gcm/gcm.jd
@@ -773,13 +773,8 @@
     <td>There was an error authenticating the sender account. <a href="#auth_error">Troubleshoot</a></td>
   </tr>
   <tr>
-    <td>500</td>
-    <td>There was an internal error in the GCM server while trying to process the request. <a href="#internal_error">Troubleshoot</a></td>
-  </tr>
-  <tr>
-    <td>503</td>
-    <td>Indicates that the server is temporarily unavailable (i.e., because of timeouts, etc ). Sender must retry later, honoring any <code>Retry-After</code> header
-      included in the response. Application servers must implement exponential back-off. The GCM server took too long to process the request. <a href="#internal_error">Troubleshoot</a></td>
+    <td>5xx</td>
+    <td>Errors in the 500-599 range (such as 500 or 503) indicate that there was an internal error in the GCM server while trying to process the request, or that the server is temporarily unavailable (for example, because of timeouts). Sender must retry later, honoring any <code>Retry-After</code> header included in the response. Application servers must implement exponential back-off. <a href="#internal_error">Troubleshoot</a></td>
   </tr>
 </table>
 
@@ -935,17 +930,15 @@
 
 Senders that cause problems risk being blacklisted. 
 <br />
-Happens when the HTTP status code is 503, or when the <code>error</code> field of a JSON object in the results array is <code>Unavailable</code>.
+Happens when the HTTP status code is between 501 and 599, or when the <code>error</code> field of a JSON object in the results array is <code>Unavailable</code>.
 </dd>
 
 <dt id="internal_error"><strong>Internal Server Error</strong></dt>
 
 <dd>
 The server encountered an error while trying to process the request. You
-could retry the same request (obeying the requirements listed in the <strong>Timeout</strong>
+could retry the same request (obeying the requirements listed in the <a href="#timeout">Timeout</a>
 section), but if the error persists, please report the problem in the <a href="https://groups.google.com/forum/?fromgroups#!forum/android-gcm">android-gcm group</a>.
-<br /> 
-Senders that cause problems risk being blacklisted. 
 <br />
 Happens when the HTTP status code is 500, or when the <code>error</code> field of a JSON
 object in the results array is <code>InternalServerError</code>.
diff --git a/docs/html/guide/google/gcm/gs.jd b/docs/html/guide/google/gcm/gs.jd
index 93eb794..8d132d8 100644
--- a/docs/html/guide/google/gcm/gs.jd
+++ b/docs/html/guide/google/gcm/gs.jd
@@ -145,7 +145,9 @@
   <li>If the value is dynamic, the service should override the <code>getSenderIds()</code> method.</li>
 </ul>
 
+
 <h4>Step 3: Write the my_app_package.GCMIntentService class</h4>
+
 <p>Next write the <code>my_app_package.GCMIntentService</code> class, overriding the following callback methods (which are called by <code>GCMBroadcastReceiver</code>):<br>
 </p>
 <ul>
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/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index abc88fa..902c82f 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -118,7 +118,10 @@
     const uint32_t yStretchCount = (mYCount + 1) >> 1;
 
     float stretchX = 0.0f;
-    float stretchY = 0.0;
+    float stretchY = 0.0f;
+
+    float rescaleX = 1.0f;
+    float rescaleY = 1.0f;
 
     const float meshWidth = right - left;
 
@@ -129,8 +132,9 @@
         }
         const float xStretchTex = stretchSize;
         const float fixed = bitmapWidth - stretchSize;
-        const float xStretch = right - left - fixed;
+        const float xStretch = fmaxf(right - left - fixed, 0.0f);
         stretchX = xStretch / xStretchTex;
+        rescaleX = fixed == 0.0f ? 0.0f : fminf(fmaxf(right - left, 0.0f) / fixed, 1.0f);
     }
 
     if (yStretchCount > 0) {
@@ -140,8 +144,9 @@
         }
         const float yStretchTex = stretchSize;
         const float fixed = bitmapHeight - stretchSize;
-        const float yStretch = bottom - top - fixed;
+        const float yStretch = fmaxf(bottom - top - fixed, 0.0f);
         stretchY = yStretch / yStretchTex;
+        rescaleY = fixed == 0.0f ? 0.0f : fminf(fmaxf(bottom - top, 0.0f) / fixed, 1.0f);
     }
 
     TextureVertex* vertex = mVertices;
@@ -160,7 +165,7 @@
         if (i & 1) {
             y2 = y1 + floorf(segment * stretchY + 0.5f);
         } else {
-            y2 = y1 + segment;
+            y2 = y1 + segment * rescaleY;
         }
 
         float vOffset = y1 == y2 ? 0.0f : 0.5 - (0.5 * segment / (y2 - y1));
@@ -172,7 +177,7 @@
             y1 += i * EXPLODE_GAP;
             y2 += i * EXPLODE_GAP;
 #endif
-            generateRow(vertex, y1, y2, v1, v2, stretchX, right - left,
+            generateRow(vertex, y1, y2, v1, v2, stretchX, rescaleX, right - left,
                     bitmapWidth, quadCount);
 #if DEBUG_EXPLODE_PATCHES
             y2 -= i * EXPLODE_GAP;
@@ -191,7 +196,8 @@
         y1 += mYCount * EXPLODE_GAP;
         y2 += mYCount * EXPLODE_GAP;
 #endif
-        generateRow(vertex, y1, y2, v1, 1.0f, stretchX, right - left, bitmapWidth, quadCount);
+        generateRow(vertex, y1, y2, v1, 1.0f, stretchX, rescaleX, right - left,
+                bitmapWidth, quadCount);
     }
 
     if (verticesCount > 0) {
@@ -212,7 +218,7 @@
 }
 
 void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, float v2,
-        float stretchX, float width, float bitmapWidth, uint32_t& quadCount) {
+        float stretchX, float rescaleX, float width, float bitmapWidth, uint32_t& quadCount) {
     float previousStepX = 0.0f;
 
     float x1 = 0.0f;
@@ -227,7 +233,7 @@
         if (i & 1) {
             x2 = x1 + floorf(segment * stretchX + 0.5f);
         } else {
-            x2 = x1 + segment;
+            x2 = x1 + segment * rescaleX;
         }
 
         float uOffset = x1 == x2 ? 0.0f : 0.5 - (0.5 * segment / (x2 - x1));
@@ -272,7 +278,7 @@
     if (y2 < 0.0f) y2 = 0.0f;
 
     // Skip degenerate and transparent (empty) quads
-    if ((mColorKey >> oldQuadCount) & 0x1) {
+    if (((mColorKey >> oldQuadCount) & 0x1) || x1 >= x2 || y1 >= y2) {
 #if DEBUG_PATCHES_EMPTY_VERTICES
         PATCH_LOGD("    quad %d (empty)", oldQuadCount);
         PATCH_LOGD("        left,  top    = %.2f, %.2f\t\tu1, v1 = %.4f, %.4f", x1, y1, u1, v1);
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
index 28c9048..0518d91 100644
--- a/libs/hwui/Patch.h
+++ b/libs/hwui/Patch.h
@@ -75,8 +75,8 @@
     void copy(const int32_t* yDivs);
 
     void generateRow(TextureVertex*& vertex, float y1, float y2,
-            float v1, float v2, float stretchX, float width, float bitmapWidth,
-            uint32_t& quadCount);
+            float v1, float v2, float stretchX, float rescaleX,
+            float width, float bitmapWidth, uint32_t& quadCount);
     void generateQuad(TextureVertex*& vertex,
             float x1, float y1, float x2, float y2,
             float u1, float v1, float u2, float v2,
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index ee17bd3..035b282 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1526,6 +1526,16 @@
 
     /**
      * @hide
+     * Checks whether speech recognition is active
+     * @return true if a recording with source {@link MediaRecorder.AudioSource#VOICE_RECOGNITION}
+     *    is underway.
+     */
+    public boolean isSpeechRecognitionActive() {
+        return AudioSystem.isSourceActive(MediaRecorder.AudioSource.VOICE_RECOGNITION);
+    }
+
+    /**
+     * @hide
      * If the stream is active locally or remotely, adjust its volume according to the enforced
      * priority rules.
      * Note: only AudioManager.STREAM_MUSIC is supported at the moment
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 103e817..260ddc7 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -111,6 +111,13 @@
     public static native boolean isStreamActive(int stream, int inPastMs);
 
     /*
+     * Checks whether the specified audio source is active.
+     *
+     * return true if any recorder using this source is currently recording
+     */
+    public static native boolean isSourceActive(int source);
+
+    /*
      * Sets a group generic audio configuration parameters. The use of these parameters
      * are platform dependent, see libaudio
      *
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 16ad74f..8e9384ee 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -152,6 +152,8 @@
                 dispatchRouteChanged(sStatic.mDefaultAudioVideo);
             }
 
+            final int mainType = mCurAudioRoutesInfo.mMainType;
+
             boolean a2dpEnabled;
             try {
                 a2dpEnabled = mAudioService.isBluetoothA2dpOn();
@@ -180,11 +182,10 @@
             }
 
             if (mBluetoothA2dpRoute != null) {
-                if (mCurAudioRoutesInfo.mMainType != AudioRoutesInfo.MAIN_SPEAKER &&
-                        mSelectedRoute == mBluetoothA2dpRoute) {
+                if (mainType != AudioRoutesInfo.MAIN_SPEAKER &&
+                        mSelectedRoute == mBluetoothA2dpRoute && !a2dpEnabled) {
                     selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo);
-                } else if (mCurAudioRoutesInfo.mMainType == AudioRoutesInfo.MAIN_SPEAKER &&
-                        (mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == null) &&
+                } else if ((mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == null) &&
                         a2dpEnabled) {
                     selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute);
                 }
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/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/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 9307f37..7e047fd 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -4302,6 +4302,9 @@
     }
 
     public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always) {
+        if (!mVibrator.hasVibrator()) {
+            return false;
+        }
         final boolean hapticsDisabled = Settings.System.getIntForUser(mContext.getContentResolver(),
                 Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) == 0;
         if (!always && (hapticsDisabled || mKeyguardMediator.isShowingAndNotHidden())) {
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 840edaf..bf7be89 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -258,6 +258,9 @@
         }
 
         public void dismiss(boolean authenticated) {
+            // If the biometric unlock was suppressed due to a user switch, it can now be safely
+            // unsuppressed because the user has left the unlock screen.
+            KeyguardUpdateMonitor.getInstance(mContext).clearBiometricUnlockUserSwitched();
             showNextSecurityScreenOrFinish(authenticated);
         }
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
index 30cd67b..e573072 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
@@ -65,7 +65,8 @@
         KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
         final boolean backupIsTimedOut = monitor.getFailedUnlockAttempts() >=
                 LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
-        return monitor.getMaxBiometricUnlockAttemptsReached() || backupIsTimedOut;
+        return monitor.getMaxBiometricUnlockAttemptsReached() || backupIsTimedOut
+                || monitor.didBiometricUnlockUserSwitch();
     }
 
     SecurityMode getSecurityMode() {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
index 15a6f9f..63a074a 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
@@ -102,6 +102,8 @@
     private int mFailedAttempts = 0;
     private int mFailedBiometricUnlockAttempts = 0;
 
+    private boolean mBiometricUnlockUserSwitched;
+
     private boolean mClockVisible;
 
     private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
@@ -404,6 +406,7 @@
                 cb.onUserSwitched(userId);
             }
         }
+        mBiometricUnlockUserSwitched = true;
         try {
             reply.sendResult(null);
         } catch (RemoteException e) {
@@ -721,6 +724,14 @@
         return mFailedBiometricUnlockAttempts >= FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP;
     }
 
+    public boolean didBiometricUnlockUserSwitch() {
+        return mBiometricUnlockUserSwitched;
+    }
+
+    public void clearBiometricUnlockUserSwitched() {
+        mBiometricUnlockUserSwitched = false;
+    }
+
     public boolean isSimLocked() {
         return isSimLocked(mSimState);
     }
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 3ffd43f..d25444f 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
@@ -201,6 +201,9 @@
         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);
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/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 85b488c..09a606e 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -991,6 +991,14 @@
                         | Notification.FLAG_NO_CLEAR;
             }
 
+            final int currentUser;
+            final long token = Binder.clearCallingIdentity();
+            try {
+                currentUser = ActivityManager.getCurrentUser();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+
             if (notification.icon != 0) {
                 final StatusBarNotification n = new StatusBarNotification(
                         pkg, id, tag, r.uid, r.initialPid, score, notification, user);
@@ -1015,7 +1023,10 @@
                         Binder.restoreCallingIdentity(identity);
                     }
                 }
-                sendAccessibilityEvent(notification, pkg);
+                // Send accessibility events only for the current user.
+                if (currentUser == userId) {
+                    sendAccessibilityEvent(notification, pkg);
+                }
             } else {
                 Slog.e(TAG, "Ignoring notification with icon==0: " + notification);
                 if (old != null && old.statusBarKey != null) {
@@ -1029,14 +1040,6 @@
                 }
             }
 
-            final int currentUser;
-            final long token = Binder.clearCallingIdentity();
-            try {
-                currentUser = ActivityManager.getCurrentUser();
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-
             // If we're not supposed to beep, vibrate, etc. then don't.
             if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
                     && (!(old != null
@@ -1066,8 +1069,9 @@
                     }
                     mSoundNotification = r;
                     // do not play notifications if stream volume is 0
-                    // (typically because ringer mode is silent).
-                    if (audioManager.getStreamVolume(audioStreamType) != 0) {
+                    // (typically because ringer mode is silent) or if speech recognition is active.
+                    if ((audioManager.getStreamVolume(audioStreamType) != 0)
+                            && !audioManager.isSpeechRecognitionActive()) {
                         final long identity = Binder.clearCallingIdentity();
                         try {
                             final IRingtonePlayer player = mAudioService.getRingtonePlayer();
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3d77b3a..7c482f5 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -643,6 +643,10 @@
         return mSecurityPolicy.mActiveWindowId;
     }
 
+    void onTouchInteractionStart() {
+        mSecurityPolicy.onTouchInteractionStart();
+    }
+
     void onTouchInteractionEnd() {
         mSecurityPolicy.onTouchInteractionEnd();
     }
@@ -2138,6 +2142,7 @@
             | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED;
 
         private int mActiveWindowId;
+        private boolean mTouchInteractionInProgress;
 
         private boolean canDispatchAccessibilityEvent(AccessibilityEvent event) {
             final int eventType = event.getEventType();
@@ -2185,12 +2190,21 @@
                     }
                 } break;
                 case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: {
-                    mActiveWindowId = windowId;
+                    // Do not allow delayed hover events to confuse us
+                    // which the active window is.
+                    if (mTouchInteractionInProgress) {
+                        mActiveWindowId = windowId;
+                    }
                 } break;
             }
         }
 
+        public void onTouchInteractionStart() {
+            mTouchInteractionInProgress = true;
+        }
+
         public void onTouchInteractionEnd() {
+            mTouchInteractionInProgress = false;
             // We want to set the active window to be current immediately
             // after the user has stopped touching the screen since if the
             // user types with the IME he should get a feedback for the
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index 2688776..dcf87350 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -398,6 +398,7 @@
 
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
+                mAms.onTouchInteractionStart();
                 // Pre-feed the motion events to the gesture detector since we
                 // have a distance slop before getting into gesture detection
                 // mode and not using the points within this slop significantly
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 6b7e29c..daed0a2 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -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");
@@ -9713,6 +9727,9 @@
         boolean onlyHistory = false;
 
         if ("history".equals(dumpPackage)) {
+            if (opti < args.length && "-s".equals(args[opti])) {
+                dumpAll = false;
+            }
             onlyHistory = true;
             dumpPackage = null;
         }
@@ -14112,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;
@@ -14141,11 +14161,13 @@
                     // 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);
@@ -14154,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() {
@@ -14188,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);
@@ -14225,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);
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 b9085a4..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;
@@ -153,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;
@@ -1662,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;
@@ -2170,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();
         }
     }
 
@@ -2227,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 037bfde..54f6deb 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -264,6 +264,9 @@
      */
     static final int DEFAULT_FADE_IN_OUT_DURATION = 400;
 
+    /** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */
+    static final int WINDOW_FREEZE_TIMEOUT_DURATION = 3000;
+
     /**
      * If true, the window manager will do its own custom freezing and general
      * management of the screen during rotation.
@@ -2749,7 +2752,10 @@
                 }
             }
 
-            if (DEBUG_LAYOUT) Slog.v(TAG, "Relayout " + win + ": viewVisibility=" + viewVisibility
+            if (DEBUG_LAYOUT
+                    // TODO: Remove once b/7094175 is fixed
+                    || ((String)win.mAttrs.getTitle()).contains("Keyguard")
+                ) Slog.v(TAG, "Relayout " + win + ": viewVisibility=" + viewVisibility
                     + " " + requestedWidth + "x" + requestedHeight + " " + win.mAttrs);
 
             win.mEnforceSizeCompat = (win.mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0;
@@ -5648,12 +5654,12 @@
     @Override
     public void showStrictModeViolation(boolean on) {
         if (mHeadless) return;
-        mH.sendMessage(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, on ? 1 : 0, 0));
+        int pid = Binder.getCallingPid();
+        mH.sendMessage(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, on ? 1 : 0, pid));
     }
 
-    private void showStrictModeViolation(int arg) {
+    private void showStrictModeViolation(int arg, int pid) {
         final boolean on = arg != 0;
-        int pid = Binder.getCallingPid();
         synchronized(mWindowMap) {
             // Ignoring requests to enable the red border from clients
             // which aren't on screen.  (e.g. Broadcast Receivers in
@@ -6018,7 +6024,8 @@
 
         mWindowsFreezingScreen = true;
         mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
-        mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_FREEZE_TIMEOUT), 2000);
+        mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_FREEZE_TIMEOUT),
+                WINDOW_FREEZE_TIMEOUT_DURATION);
         mWaitingForConfig = true;
         getDefaultDisplayContentLocked().layoutNeeded = true;
         startFreezingDisplayLocked(inTransaction, 0, 0);
@@ -7643,7 +7650,7 @@
                 }
 
                 case SHOW_STRICT_MODE_VIOLATION: {
-                    showStrictModeViolation(msg.arg1);
+                    showStrictModeViolation(msg.arg1, msg.arg2);
                     break;
                 }
 
@@ -8380,7 +8387,7 @@
                 // when we first froze the display.
                 mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
                 mH.sendMessageDelayed(mH.obtainMessage(
-                        H.WINDOW_FREEZE_TIMEOUT), 2000);
+                        H.WINDOW_FREEZE_TIMEOUT), WINDOW_FREEZE_TIMEOUT_DURATION);
             }
         }
     }
@@ -9268,7 +9275,9 @@
                         "Reporting new frame to " + win + ": " + win.mCompatFrame);
                 int diff = 0;
                 boolean configChanged = win.isConfigChanged();
-                if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION)
+                if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION
+                        // TODO: Remove once b/7094175 is fixed
+                        || ((String)win.mAttrs.getTitle()).contains("Keyguard"))
                         && configChanged) {
                     Slog.i(TAG, "Sending new config to window " + win + ": "
                             + winAnimator.mSurfaceW + "x" + winAnimator.mSurfaceH