Merge "Suppressing FUL after user switch (fix b/7316467)" into jb-mr1-dev
diff --git a/api/17.txt b/api/17.txt
index f8ad4d9..9af3b49 100644
--- a/api/17.txt
+++ b/api/17.txt
@@ -16620,6 +16620,8 @@
     method public android.os.UserHandle getUserForSerialNumber(long);
     method public java.lang.String getUserName();
     method public boolean isUserAGoat();
+    method public boolean isUserRunning(android.os.UserHandle);
+    method public boolean isUserRunningOrStopping(android.os.UserHandle);
   }
 
   public abstract class Vibrator {
@@ -18903,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 f8ad4d9..9af3b49 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -16620,6 +16620,8 @@
     method public android.os.UserHandle getUserForSerialNumber(long);
     method public java.lang.String getUserName();
     method public boolean isUserAGoat();
+    method public boolean isUserRunning(android.os.UserHandle);
+    method public boolean isUserRunningOrStopping(android.os.UserHandle);
   }
 
   public abstract class Vibrator {
@@ -18903,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/cmds/settings/Android.mk b/cmds/settings/Android.mk
new file mode 100644
index 0000000..05deb99
--- /dev/null
+++ b/cmds/settings/Android.mk
@@ -0,0 +1,18 @@
+# Copyright 2011 The Android Open Source Project
+#
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_MODULE := settings
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := settings
+LOCAL_SRC_FILES := settings
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_PREBUILT)
+
+
diff --git a/cmds/settings/settings b/cmds/settings/settings
new file mode 100755
index 0000000..ef459ca
--- /dev/null
+++ b/cmds/settings/settings
@@ -0,0 +1,5 @@
+# Script to start "settings" on the device
+#
+base=/system
+export CLASSPATH=$base/framework/settings.jar
+exec app_process $base/bin com.android.commands.settings.SettingsCmd "$@"
diff --git a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
new file mode 100644
index 0000000..0c69f01
--- /dev/null
+++ b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.commands.settings;
+
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.app.IActivityManager.ContentProviderHolder;
+import android.content.IContentProvider;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+public final class SettingsCmd {
+    static final String TAG = "settings";
+
+    enum CommandVerb {
+        UNSPECIFIED,
+        GET,
+        PUT
+    }
+
+    static String[] mArgs;
+    int mNextArg;
+    int mUser = -1;     // unspecified
+    CommandVerb mVerb = CommandVerb.UNSPECIFIED;
+    String mTable = null;
+    String mKey = null;
+    String mValue = null;
+
+    public static void main(String[] args) {
+        if (args == null || args.length < 3) {
+            printUsage();
+            return;
+        }
+
+        mArgs = args;
+        try {
+            new SettingsCmd().run();
+        } catch (Exception e) {
+            System.err.println("Unable to run settings command");
+        }
+    }
+
+    public void run() {
+        boolean valid = false;
+        String arg;
+        try {
+            while ((arg = nextArg()) != null) {
+                if ("--user".equals(arg)) {
+                    if (mUser != -1) {
+                        // --user specified more than once; invalid
+                        break;
+                    }
+                    mUser = Integer.parseInt(nextArg());
+                } else if (mVerb == CommandVerb.UNSPECIFIED) {
+                    if ("get".equalsIgnoreCase(arg)) {
+                        mVerb = CommandVerb.GET;
+                    } else if ("put".equalsIgnoreCase(arg)) {
+                        mVerb = CommandVerb.PUT;
+                    } else {
+                        // invalid
+                        System.err.println("Invalid command: " + arg);
+                        break;
+                    }
+                } else if (mTable == null) {
+                    if (!"system".equalsIgnoreCase(arg)
+                            && !"secure".equalsIgnoreCase(arg)
+                            && !"global".equalsIgnoreCase(arg)) {
+                        System.err.println("Invalid namespace '" + arg + "'");
+                        break;  // invalid
+                    }
+                    mTable = arg.toLowerCase();
+                } else if (mVerb == CommandVerb.GET) {
+                    mKey = arg;
+                    if (mNextArg >= mArgs.length) {
+                        valid = true;
+                    } else {
+                        System.err.println("Too many arguments");
+                    }
+                    break;
+                } else if (mKey == null) {
+                    mKey = arg;
+                    // keep going; there's another PUT arg
+                } else {    // PUT, final arg
+                    mValue = arg;
+                    if (mNextArg >= mArgs.length) {
+                        valid = true;
+                    } else {
+                        System.err.println("Too many arguments");
+                    }
+                    break;
+                }
+            }
+        } catch (Exception e) {
+            valid = false;
+        }
+
+        if (valid) {
+            if (mUser < 0) {
+                mUser = UserHandle.USER_OWNER;
+            }
+
+            try {
+                IActivityManager activityManager = ActivityManagerNative.getDefault();
+                IContentProvider provider = null;
+                IBinder token = new Binder();
+                try {
+                    ContentProviderHolder holder = activityManager.getContentProviderExternal(
+                            "settings", UserHandle.USER_OWNER, token);
+                    if (holder == null) {
+                        throw new IllegalStateException("Could not find settings provider");
+                    }
+                    provider = holder.provider;
+
+                    switch (mVerb) {
+                        case GET:
+                            System.out.println(getForUser(provider, mUser, mTable, mKey));
+                            break;
+                        case PUT:
+                            putForUser(provider, mUser, mTable, mKey, mValue);
+                            break;
+                        default:
+                            System.err.println("Unspecified command");
+                            break;
+                    }
+
+                } finally {
+                    if (provider != null) {
+                        activityManager.removeContentProviderExternal("settings", token);
+                    }
+                }
+            } catch (Exception e) {
+                System.err.println("Error while accessing settings provider");
+                e.printStackTrace();
+            }
+
+        } else {
+            printUsage();
+        }
+    }
+
+    private String nextArg() {
+        if (mNextArg >= mArgs.length) {
+            return null;
+        }
+        String arg = mArgs[mNextArg];
+        mNextArg++;
+        return arg;
+    }
+
+    String getForUser(IContentProvider provider, int userHandle,
+            final String table, final String key) {
+        final String callGetCommand;
+        if ("system".equals(table)) callGetCommand = Settings.CALL_METHOD_GET_SYSTEM;
+        else if ("secure".equals(table)) callGetCommand = Settings.CALL_METHOD_GET_SECURE;
+        else if ("global".equals(table)) callGetCommand = Settings.CALL_METHOD_GET_GLOBAL;
+        else {
+            System.err.println("Invalid table; no put performed");
+            throw new IllegalArgumentException("Invalid table " + table);
+        }
+
+        String result = null;
+        try {
+            Bundle arg = new Bundle();
+            arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
+            Bundle b = provider.call(callGetCommand, key, arg);
+            if (b != null) {
+                result = b.getPairValue();
+            }
+        } catch (RemoteException e) {
+            System.err.println("Can't read key " + key + " in " + table + " for user " + userHandle);
+        }
+        return result;
+    }
+
+    void putForUser(IContentProvider provider, int userHandle,
+            final String table, final String key, final String value) {
+        final String callPutCommand;
+        if ("system".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_SYSTEM;
+        else if ("secure".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_SECURE;
+        else if ("global".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_GLOBAL;
+        else {
+            System.err.println("Invalid table; no put performed");
+            return;
+        }
+
+        try {
+            Bundle arg = new Bundle();
+            arg.putString(Settings.NameValueTable.VALUE, value);
+            arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
+            provider.call(callPutCommand, key, arg);
+        } catch (RemoteException e) {
+            System.err.println("Can't set key " + key + " in " + table + " for user " + userHandle);
+        }
+    }
+
+    private static void printUsage() {
+        System.err.println("usage:  settings [--user NUM] get namespace key");
+        System.err.println("        settings [--user NUM] put namespace key value");
+        System.err.println("\n'namespace' is one of {system, secure, global}, case-insensitive");
+        System.err.println("If '--user NUM' is not given, the operations are performed on the owner user.");
+    }
+}
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/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 0eda6b4..594be68 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1981,7 +1981,7 @@
      */
     public boolean isUserRunning(int userid) {
         try {
-            return ActivityManagerNative.getDefault().isUserRunning(userid);
+            return ActivityManagerNative.getDefault().isUserRunning(userid, false);
         } catch (RemoteException e) {
             return false;
         }
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index bb62c9e..7492629 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1608,7 +1608,8 @@
         case IS_USER_RUNNING_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             int userid = data.readInt();
-            boolean result = isUserRunning(userid);
+            boolean orStopping = data.readInt() != 0;
+            boolean result = isUserRunning(userid, orStopping);
             reply.writeNoException();
             reply.writeInt(result ? 1 : 0);
             return true;
@@ -3865,11 +3866,12 @@
         return userInfo;
     }
 
-    public boolean isUserRunning(int userid) throws RemoteException {
+    public boolean isUserRunning(int userid, boolean orStopping) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeInt(userid);
+        data.writeInt(orStopping ? 1 : 0);
         mRemote.transact(IS_USER_RUNNING_TRANSACTION, data, reply, 0);
         reply.readException();
         boolean result = reply.readInt() != 0;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index c324da92..3e1e358 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1352,10 +1352,16 @@
             ComponentName cn = ActivityManagerNative.getDefault().startService(
                 mMainThread.getApplicationThread(), service,
                 service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
-            if (cn != null && cn.getPackageName().equals("!")) {
-                throw new SecurityException(
-                        "Not allowed to start service " + service
-                        + " without permission " + cn.getClassName());
+            if (cn != null) {
+                if (cn.getPackageName().equals("!")) {
+                    throw new SecurityException(
+                            "Not allowed to start service " + service
+                            + " without permission " + cn.getClassName());
+                } else if (cn.getPackageName().equals("!!")) {
+                    throw new SecurityException(
+                            "Unable to start service " + service
+                            + ": " + cn.getClassName());
+                }
             }
             return cn;
         } catch (RemoteException e) {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index da844ef..97250e9 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -326,7 +326,7 @@
     public boolean switchUser(int userid) throws RemoteException;
     public int stopUser(int userid, IStopUserCallback callback) throws RemoteException;
     public UserInfo getCurrentUser() throws RemoteException;
-    public boolean isUserRunning(int userid) throws RemoteException;
+    public boolean isUserRunning(int userid, boolean orStopping) throws RemoteException;
     public int[] getRunningUserIds() throws RemoteException;
 
     public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException;
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 6e2278d..6fdf3b4 100755
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -45,6 +45,7 @@
 public final class BluetoothA2dp implements BluetoothProfile {
     private static final String TAG = "BluetoothA2dp";
     private static final boolean DBG = true;
+    private static final boolean VDBG = false;
 
     /**
      * Intent used to broadcast the change in connection state of the A2DP
@@ -113,7 +114,7 @@
                 public void onBluetoothStateChange(boolean up) {
                     if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
                     if (!up) {
-                        if (DBG) Log.d(TAG,"Unbinding service...");
+                        if (VDBG) Log.d(TAG,"Unbinding service...");
                         synchronized (mConnection) {
                             try {
                                 mService = null;
@@ -126,7 +127,7 @@
                         synchronized (mConnection) {
                             try {
                                 if (mService == null) {
-                                    if (DBG) Log.d(TAG,"Binding service...");
+                                    if (VDBG) Log.d(TAG,"Binding service...");
                                     if (!mContext.bindService(new Intent(IBluetoothA2dp.class.getName()), mConnection, 0)) {
                                         Log.e(TAG, "Could not bind to Bluetooth A2DP Service");
                                     }
@@ -269,7 +270,7 @@
      * {@inheritDoc}
      */
     public List<BluetoothDevice> getConnectedDevices() {
-        if (DBG) log("getConnectedDevices()");
+        if (VDBG) log("getConnectedDevices()");
         if (mService != null && isEnabled()) {
             try {
                 return mService.getConnectedDevices();
@@ -286,7 +287,7 @@
      * {@inheritDoc}
      */
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (DBG) log("getDevicesMatchingStates()");
+        if (VDBG) log("getDevicesMatchingStates()");
         if (mService != null && isEnabled()) {
             try {
                 return mService.getDevicesMatchingConnectionStates(states);
@@ -303,7 +304,7 @@
      * {@inheritDoc}
      */
     public int getConnectionState(BluetoothDevice device) {
-        if (DBG) log("getState(" + device + ")");
+        if (VDBG) log("getState(" + device + ")");
         if (mService != null && isEnabled()
             && isValidDevice(device)) {
             try {
@@ -365,7 +366,7 @@
      * @hide
      */
     public int getPriority(BluetoothDevice device) {
-        if (DBG) log("getPriority(" + device + ")");
+        if (VDBG) log("getPriority(" + device + ")");
         if (mService != null && isEnabled()
             && isValidDevice(device)) {
             try {
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 541b69f..793d798 100755
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -46,6 +46,7 @@
 public final class BluetoothHeadset implements BluetoothProfile {
     private static final String TAG = "BluetoothHeadset";
     private static final boolean DBG = true;
+    private static final boolean VDBG = false;
 
     /**
      * Intent used to broadcast the change in connection state of the Headset
@@ -226,7 +227,7 @@
                 public void onBluetoothStateChange(boolean up) {
                     if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
                     if (!up) {
-                        if (DBG) Log.d(TAG,"Unbinding service...");
+                        if (VDBG) Log.d(TAG,"Unbinding service...");
                         synchronized (mConnection) {
                             try {
                                 mService = null;
@@ -239,7 +240,7 @@
                         synchronized (mConnection) {
                             try {
                                 if (mService == null) {
-                                    if (DBG) Log.d(TAG,"Binding service...");
+                                    if (VDBG) Log.d(TAG,"Binding service...");
                                     if (!mContext.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) {
                                         Log.e(TAG, "Could not bind to Bluetooth Headset Service");
                                     }
@@ -281,7 +282,7 @@
      * are ok.
      */
     /*package*/ void close() {
-        if (DBG) log("close()");
+        if (VDBG) log("close()");
 
         IBluetoothManager mgr = mAdapter.getBluetoothManager();
         if (mgr != null) {
@@ -387,7 +388,7 @@
      * {@inheritDoc}
      */
     public List<BluetoothDevice> getConnectedDevices() {
-        if (DBG) log("getConnectedDevices()");
+        if (VDBG) log("getConnectedDevices()");
         if (mService != null && isEnabled()) {
             try {
                 return mService.getConnectedDevices();
@@ -404,7 +405,7 @@
      * {@inheritDoc}
      */
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (DBG) log("getDevicesMatchingStates()");
+        if (VDBG) log("getDevicesMatchingStates()");
         if (mService != null && isEnabled()) {
             try {
                 return mService.getDevicesMatchingConnectionStates(states);
@@ -421,7 +422,7 @@
      * {@inheritDoc}
      */
     public int getConnectionState(BluetoothDevice device) {
-        if (DBG) log("getConnectionState(" + device + ")");
+        if (VDBG) log("getConnectionState(" + device + ")");
         if (mService != null && isEnabled() &&
             isValidDevice(device)) {
             try {
@@ -483,7 +484,7 @@
      * @hide
      */
     public int getPriority(BluetoothDevice device) {
-        if (DBG) log("getPriority(" + device + ")");
+        if (VDBG) log("getPriority(" + device + ")");
         if (mService != null && isEnabled() &&
             isValidDevice(device)) {
             try {
@@ -566,7 +567,7 @@
      *         false otherwise or on error
      */
     public boolean isAudioConnected(BluetoothDevice device) {
-        if (DBG) log("isAudioConnected()");
+        if (VDBG) log("isAudioConnected()");
         if (mService != null && isEnabled() &&
             isValidDevice(device)) {
             try {
@@ -594,7 +595,7 @@
      * @hide
      */
     public int getBatteryUsageHint(BluetoothDevice device) {
-        if (DBG) log("getBatteryUsageHint()");
+        if (VDBG) log("getBatteryUsageHint()");
         if (mService != null && isEnabled() &&
             isValidDevice(device)) {
             try {
@@ -661,7 +662,7 @@
      * @hide
      */
     public int getAudioState(BluetoothDevice device) {
-        if (DBG) log("getAudioState");
+        if (VDBG) log("getAudioState");
         if (mService != null && !isDisabled()) {
             try {
                 return mService.getAudioState(device);
@@ -683,7 +684,7 @@
      * @hide
      */
     public boolean isAudioOn() {
-        if (DBG) log("isAudioOn()");
+        if (VDBG) log("isAudioOn()");
         if (mService != null && isEnabled()) {
             try {
               return mService.isAudioOn();
diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java
index 4a0bc7e..cb23662 100644
--- a/core/java/android/bluetooth/BluetoothHealth.java
+++ b/core/java/android/bluetooth/BluetoothHealth.java
@@ -58,6 +58,7 @@
 public final class BluetoothHealth implements BluetoothProfile {
     private static final String TAG = "BluetoothHealth";
     private static final boolean DBG = true;
+    private static final boolean VDBG = false;
 
     /**
      * Health Profile Source Role - the health device.
@@ -102,7 +103,7 @@
                 public void onBluetoothStateChange(boolean up) {
                     if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
                     if (!up) {
-                        if (DBG) Log.d(TAG,"Unbinding service...");
+                        if (VDBG) Log.d(TAG,"Unbinding service...");
                         synchronized (mConnection) {
                             try {
                                 mService = null;
@@ -115,7 +116,7 @@
                         synchronized (mConnection) {
                             try {
                                 if (mService == null) {
-                                    if (DBG) Log.d(TAG,"Binding service...");
+                                    if (VDBG) Log.d(TAG,"Binding service...");
                                     if (!mContext.bindService(new Intent(IBluetoothHealth.class.getName()), mConnection, 0)) {
                                         Log.e(TAG, "Could not bind to Bluetooth Health Service");
                                     }
@@ -148,7 +149,7 @@
             BluetoothHealthCallback callback) {
         if (!isEnabled() || name == null) return false;
 
-        if (DBG) log("registerSinkApplication(" + name + ":" + dataType + ")");
+        if (VDBG) log("registerSinkApplication(" + name + ":" + dataType + ")");
         return registerAppConfiguration(name, dataType, SINK_ROLE,
                 CHANNEL_TYPE_ANY, callback);
     }
@@ -174,7 +175,7 @@
         boolean result = false;
         if (!isEnabled() || !checkAppParam(name, role, channelType, callback)) return result;
 
-        if (DBG) log("registerApplication(" + name + ":" + dataType + ")");
+        if (VDBG) log("registerApplication(" + name + ":" + dataType + ")");
         BluetoothHealthCallbackWrapper wrapper = new BluetoothHealthCallbackWrapper(callback);
         BluetoothHealthAppConfiguration config =
                 new BluetoothHealthAppConfiguration(name, dataType, role, channelType);
@@ -488,7 +489,7 @@
     }
 
     /*package*/ void close() {
-        if (DBG) log("close()");
+        if (VDBG) log("close()");
         IBluetoothManager mgr = mAdapter.getBluetoothManager();
         if (mgr != null) {
             try {
diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothInputDevice.java
index bff966d..db7e424 100755
--- a/core/java/android/bluetooth/BluetoothInputDevice.java
+++ b/core/java/android/bluetooth/BluetoothInputDevice.java
@@ -45,6 +45,7 @@
 public final class BluetoothInputDevice implements BluetoothProfile {
     private static final String TAG = "BluetoothInputDevice";
     private static final boolean DBG = true;
+    private static final boolean VDBG = false;
 
     /**
      * Intent used to broadcast the change in connection state of the Input
@@ -191,7 +192,7 @@
                 public void onBluetoothStateChange(boolean up) {
                     if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
                     if (!up) {
-                        if (DBG) Log.d(TAG,"Unbinding service...");
+                        if (VDBG) Log.d(TAG,"Unbinding service...");
                         synchronized (mConnection) {
                             try {
                                 mService = null;
@@ -204,7 +205,7 @@
                         synchronized (mConnection) {
                             try {
                                 if (mService == null) {
-                                    if (DBG) Log.d(TAG,"Binding service...");
+                                    if (VDBG) Log.d(TAG,"Binding service...");
                                     if (!mContext.bindService(new Intent(IBluetoothInputDevice.class.getName()), mConnection, 0)) {
                                         Log.e(TAG, "Could not bind to Bluetooth HID Service");
                                     }
@@ -243,7 +244,7 @@
     }
 
     /*package*/ void close() {
-        if (DBG) log("close()");
+        if (VDBG) log("close()");
         IBluetoothManager mgr = mAdapter.getBluetoothManager();
         if (mgr != null) {
             try {
@@ -344,7 +345,7 @@
      * {@inheritDoc}
      */
     public List<BluetoothDevice> getConnectedDevices() {
-        if (DBG) log("getConnectedDevices()");
+        if (VDBG) log("getConnectedDevices()");
         if (mService != null && isEnabled()) {
             try {
                 return mService.getConnectedDevices();
@@ -361,7 +362,7 @@
      * {@inheritDoc}
      */
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (DBG) log("getDevicesMatchingStates()");
+        if (VDBG) log("getDevicesMatchingStates()");
         if (mService != null && isEnabled()) {
             try {
                 return mService.getDevicesMatchingConnectionStates(states);
@@ -378,7 +379,7 @@
      * {@inheritDoc}
      */
     public int getConnectionState(BluetoothDevice device) {
-        if (DBG) log("getState(" + device + ")");
+        if (VDBG) log("getState(" + device + ")");
         if (mService != null && isEnabled() && isValidDevice(device)) {
             try {
                 return mService.getConnectionState(device);
@@ -438,7 +439,7 @@
      * @hide
      */
     public int getPriority(BluetoothDevice device) {
-        if (DBG) log("getPriority(" + device + ")");
+        if (VDBG) log("getPriority(" + device + ")");
         if (mService != null && isEnabled() && isValidDevice(device)) {
             try {
                 return mService.getPriority(device);
@@ -519,7 +520,7 @@
     * @hide
     */
     public boolean getProtocolMode(BluetoothDevice device) {
-        if (DBG) log("getProtocolMode(" + device + ")");
+        if (VDBG) log("getProtocolMode(" + device + ")");
         if (mService != null && isEnabled() && isValidDevice(device)) {
             try {
                 return mService.getProtocolMode(device);
@@ -570,7 +571,7 @@
      * @hide
      */
     public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) {
-        if (DBG) log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + "bufferSize=" + bufferSize);
+        if (VDBG) log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + "bufferSize=" + bufferSize);
         if (mService != null && isEnabled() && isValidDevice(device)) {
             try {
                 return mService.getReport(device, reportType, reportId, bufferSize);
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index cae7a73..e25ec86 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -44,6 +44,7 @@
 public final class BluetoothPan implements BluetoothProfile {
     private static final String TAG = "BluetoothPan";
     private static final boolean DBG = true;
+    private static final boolean VDBG = false;
 
     /**
      * Intent used to broadcast the change in connection state of the Pan
@@ -145,7 +146,7 @@
     }
 
     /*package*/ void close() {
-        if (DBG) log("close()");
+        if (VDBG) log("close()");
         if (mConnection != null) {
             mContext.unbindService(mConnection);
             mConnection = null;
@@ -175,7 +176,7 @@
                 }
                 Log.d(TAG, "BluetoothPan(), bindService called");
             } else {
-                if (DBG) Log.d(TAG,"Unbinding service...");
+                if (VDBG) Log.d(TAG,"Unbinding service...");
                 synchronized (mConnection) {
                     try {
                         mPanService = null;
@@ -266,7 +267,7 @@
      * {@inheritDoc}
      */
     public List<BluetoothDevice> getConnectedDevices() {
-        if (DBG) log("getConnectedDevices()");
+        if (VDBG) log("getConnectedDevices()");
         if (mPanService != null && isEnabled()) {
             try {
                 return mPanService.getConnectedDevices();
@@ -283,7 +284,7 @@
      * {@inheritDoc}
      */
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (DBG) log("getDevicesMatchingStates()");
+        if (VDBG) log("getDevicesMatchingStates()");
         if (mPanService != null && isEnabled()) {
             try {
                 return mPanService.getDevicesMatchingConnectionStates(states);
@@ -300,7 +301,7 @@
      * {@inheritDoc}
      */
     public int getConnectionState(BluetoothDevice device) {
-        if (DBG) log("getState(" + device + ")");
+        if (VDBG) log("getState(" + device + ")");
         if (mPanService != null && isEnabled()
             && isValidDevice(device)) {
             try {
@@ -324,7 +325,7 @@
     }
 
     public boolean isTetheringOn() {
-        if (DBG) log("isTetheringOn()");
+        if (VDBG) log("isTetheringOn()");
         try {
             return mPanService.isTetheringOn();
         } catch (RemoteException e) {
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index 7de2ef6..b5280e5 100755
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -51,7 +51,8 @@
 public class BluetoothPbap {
 
     private static final String TAG = "BluetoothPbap";
-    private static final boolean DBG = false;
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false;
 
     /** int extra for PBAP_STATE_CHANGED_ACTION */
     public static final String PBAP_STATE =
@@ -114,7 +115,7 @@
                 public void onBluetoothStateChange(boolean up) {
                     if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
                     if (!up) {
-                        if (DBG) Log.d(TAG,"Unbinding service...");
+                        if (VDBG) Log.d(TAG,"Unbinding service...");
                         synchronized (mConnection) {
                             try {
                                 mService = null;
@@ -127,7 +128,7 @@
                         synchronized (mConnection) {
                             try {
                                 if (mService == null) {
-                                    if (DBG) Log.d(TAG,"Binding service...");
+                                    if (VDBG) Log.d(TAG,"Binding service...");
                                     if (!mContext.bindService(
                                                         new Intent(IBluetoothPbap.class.getName()),
                                                         mConnection, 0)) {
@@ -206,7 +207,7 @@
      *         object is currently not connected to the Pbap service.
      */
     public int getState() {
-        if (DBG) log("getState()");
+        if (VDBG) log("getState()");
         if (mService != null) {
             try {
                 return mService.getState();
@@ -225,7 +226,7 @@
      *         the Pbap service.
      */
     public BluetoothDevice getClient() {
-        if (DBG) log("getClient()");
+        if (VDBG) log("getClient()");
         if (mService != null) {
             try {
                 return mService.getClient();
@@ -243,7 +244,7 @@
      * object is not currently connected to the Pbap service.
      */
     public boolean isConnected(BluetoothDevice device) {
-        if (DBG) log("isConnected(" + device + ")");
+        if (VDBG) log("isConnected(" + device + ")");
         if (mService != null) {
             try {
                 return mService.isConnected(device);
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 1bc640f..aba8710 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -72,6 +72,8 @@
  */
 public final class BluetoothSocket implements Closeable {
     private static final String TAG = "BluetoothSocket";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false;
 
     /** @hide */
     public static final int MAX_RFCOMM_CHANNEL = 30;
@@ -172,7 +174,7 @@
         BluetoothSocket as = new BluetoothSocket(this);
         as.mSocketState = SocketState.CONNECTED;
         FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors();
-        Log.d(TAG, "socket fd passed by stack  fds: " + fds);
+        if (VDBG) Log.d(TAG, "socket fd passed by stack  fds: " + fds);
         if(fds == null || fds.length != 1) {
             Log.e(TAG, "socket fd passed from stack failed, fds: " + fds);
             throw new IOException("bt socket acept failed");
@@ -291,7 +293,7 @@
                     mUuid, mPort, getSecurityFlags());
             synchronized(this)
             {
-                Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
+                if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
                 if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
                 if (mPfd == null) throw new IOException("bt socket connect failed");
                 FileDescriptor fd = mPfd.getFileDescriptor();
@@ -339,23 +341,24 @@
         // read out port number
         try {
             synchronized(this) {
-                Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
+                if (VDBG) Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " +
+                                mPfd);
                 if(mSocketState != SocketState.INIT) return EBADFD;
                 if(mPfd == null) return -1;
                 FileDescriptor fd = mPfd.getFileDescriptor();
-                Log.d(TAG, "bindListen(), new LocalSocket ");
+                if (VDBG) Log.d(TAG, "bindListen(), new LocalSocket ");
                 mSocket = new LocalSocket(fd);
-                Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() ");
+                if (VDBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() ");
                 mSocketIS = mSocket.getInputStream();
                 mSocketOS = mSocket.getOutputStream();
             }
-            Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS);
+            if (VDBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS);
             int channel = readInt(mSocketIS);
             synchronized(this) {
                 if(mSocketState == SocketState.INIT)
                     mSocketState = SocketState.LISTENING;
             }
-            Log.d(TAG, "channel: " + channel);
+            if (VDBG) Log.d(TAG, "channel: " + channel);
             if (mPort == -1) {
                 mPort = channel;
             } // else ASSERT(mPort == channel)
@@ -385,26 +388,26 @@
     }
 
     /*package*/ int available() throws IOException {
-        Log.d(TAG, "available: " + mSocketIS);
+        if (VDBG) Log.d(TAG, "available: " + mSocketIS);
         return mSocketIS.available();
     }
 
     /*package*/ int read(byte[] b, int offset, int length) throws IOException {
 
-            Log.d(TAG, "read in:  " + mSocketIS + " len: " + length);
+            if (VDBG) Log.d(TAG, "read in:  " + mSocketIS + " len: " + length);
             int ret = mSocketIS.read(b, offset, length);
             if(ret < 0)
                 throw new IOException("bt socket closed, read return: " + ret);
-            Log.d(TAG, "read out:  " + mSocketIS + " ret: " + ret);
+            if (VDBG) Log.d(TAG, "read out:  " + mSocketIS + " ret: " + ret);
             return ret;
     }
 
     /*package*/ int write(byte[] b, int offset, int length) throws IOException {
 
-            Log.d(TAG, "write: " + mSocketOS + " length: " + length);
+            if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
             mSocketOS.write(b, offset, length);
             // There is no good way to confirm since the entire process is asynchronous anyway
-            Log.d(TAG, "write out: " + mSocketOS + " length: " + length);
+            if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length);
             return length;
     }
 
@@ -420,10 +423,10 @@
                  if(mSocketState == SocketState.CLOSED)
                     return;
                  mSocketState = SocketState.CLOSED;
-                 Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS +
+                 if (VDBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS +
                         ", mSocketOS: " + mSocketOS + "mSocket: " + mSocket);
                  if(mSocket != null) {
-                    Log.d(TAG, "Closing mSocket: " + mSocket);
+                    if (VDBG) Log.d(TAG, "Closing mSocket: " + mSocket);
                     mSocket.shutdownInput();
                     mSocket.shutdownOutput();
                     mSocket.close();
@@ -449,7 +452,7 @@
     private String waitSocketSignal(InputStream is) throws IOException {
         byte [] sig = new byte[SOCK_SIGNAL_SIZE];
         int ret = readAll(is, sig);
-        Log.d(TAG, "waitSocketSignal read 16 bytes signal ret: " + ret);
+        if (VDBG) Log.d(TAG, "waitSocketSignal read 16 bytes signal ret: " + ret);
         ByteBuffer bb = ByteBuffer.wrap(sig);
         bb.order(ByteOrder.nativeOrder());
         int size = bb.getShort();
@@ -458,7 +461,7 @@
         int channel = bb.getInt();
         int status = bb.getInt();
         String RemoteAddr = convertAddr(addr);
-        Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: "
+        if (VDBG) Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: "
                 + RemoteAddr + ", channel: " + channel + ", status: " + status);
         if(status != 0)
             throw new IOException("Connection failure, status: " + status);
@@ -481,7 +484,7 @@
     private int readInt(InputStream is) throws IOException {
         byte[] ibytes = new byte[4];
         int ret = readAll(is, ibytes);
-        Log.d(TAG, "inputStream.read ret: " + ret);
+        if (VDBG) Log.d(TAG, "inputStream.read ret: " + ret);
         ByteBuffer bb = ByteBuffer.wrap(ibytes);
         bb.order(ByteOrder.nativeOrder());
         return bb.getInt();
diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
index 30406e9..063e5a8 100644
--- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
+++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
@@ -51,6 +51,8 @@
 public class BluetoothTetheringDataTracker implements NetworkStateTracker {
     private static final String NETWORKTYPE = "BLUETOOTH_TETHER";
     private static final String TAG = "BluetoothTethering";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false;
 
     private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
     private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
@@ -99,10 +101,10 @@
      * Begin monitoring connectivity
      */
     public void startMonitoring(Context context, Handler target) {
-        Log.d(TAG, "startMonitoring: target: " + target);
+        if (DBG) Log.d(TAG, "startMonitoring: target: " + target);
         mContext = context;
         mCsHandler = target;
-        Log.d(TAG, "startMonitoring: mCsHandler: " + mCsHandler);
+        if (VDBG) Log.d(TAG, "startMonitoring: mCsHandler: " + mCsHandler);
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         if (adapter != null) {
             adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN);
@@ -310,31 +312,31 @@
     }
     public synchronized void startReverseTether(String iface) {
         mIface = iface;
-        Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler);
+        if (DBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler);
          mDhcpThread = new Thread(new Runnable() {
             public void run() {
                 //TODO(): Add callbacks for failure and success case.
                 //Currently this thread runs independently.
-                Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler);
+                if (DBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler);
                 String DhcpResultName = "dhcp." + mIface + ".result";;
                 String result = "";
-                Log.d(TAG, "waiting for change of sys prop dhcp result: " + DhcpResultName);
+                if (VDBG) Log.d(TAG, "waiting for change of sys prop dhcp result: " + DhcpResultName);
                 for(int i = 0; i < 30*5; i++) {
                     try { Thread.sleep(200); } catch (InterruptedException ie) { return;}
                     result = SystemProperties.get(DhcpResultName);
-                    Log.d(TAG, "read " + DhcpResultName + ": " + result);
+                    if (VDBG) Log.d(TAG, "read " + DhcpResultName + ": " + result);
                     if(result.equals("failed")) {
                         Log.e(TAG, "startReverseTether, failed to start dhcp service");
                         return;
                     }
                     if(result.equals("ok")) {
-                        Log.d(TAG, "startReverseTether, dhcp resut: " + result);
+                        if (VDBG) Log.d(TAG, "startReverseTether, dhcp resut: " + result);
                         if(readLinkProperty(mIface)) {
 
                             mNetworkInfo.setIsAvailable(true);
                             mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
 
-                            Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler);
+                            if (VDBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler);
                             if(mCsHandler != null) {
                                 Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
                                 msg.sendToTarget();
@@ -346,7 +348,7 @@
                         return;
                     }
                 }
-                Log.d(TAG, "startReverseTether, dhcp failed, resut: " + result);
+                Log.e(TAG, "startReverseTether, dhcp failed, resut: " + result);
             }
         });
         mDhcpThread.start();
diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java
index 0f6488a..4512e82 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -345,10 +345,11 @@
     public SyncAdapterType[] getSyncAdapterTypes() {
         // This makes it so that future permission checks will be in the context of this
         // process rather than the caller's process. We will restore this before returning.
-        long identityToken = clearCallingIdentity();
+        final int userId = UserHandle.getCallingUserId();
+        final long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
-            return syncManager.getSyncAdapterTypes();
+            return syncManager.getSyncAdapterTypes(userId);
         } finally {
             restoreCallingIdentity(identityToken);
         }
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 564a804..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/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 1e8671b..6624eb8 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -18,13 +18,18 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
 import android.graphics.ImageFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
+import android.media.IAudioService;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.util.Log;
 import android.text.TextUtils;
 import android.view.Surface;
@@ -192,7 +197,21 @@
      * Returns the information about a particular camera.
      * If {@link #getNumberOfCameras()} returns N, the valid id is 0 to N-1.
      */
-    public native static void getCameraInfo(int cameraId, CameraInfo cameraInfo);
+    public static void getCameraInfo(int cameraId, CameraInfo cameraInfo) {
+        _getCameraInfo(cameraId, cameraInfo);
+        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+        IAudioService audioService = IAudioService.Stub.asInterface(b);
+        try {
+            if (audioService.isCameraSoundForced()) {
+                // Only set this when sound is forced; otherwise let native code
+                // decide.
+                cameraInfo.canDisableShutterSound = false;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Audio service is unavailable for queries");
+        }
+    }
+    private native static void _getCameraInfo(int cameraId, CameraInfo cameraInfo);
 
     /**
      * Information about a camera
@@ -1185,7 +1204,20 @@
      * @see CameraInfo#canDisableShutterSound
      * @see ShutterCallback
      */
-    public native final boolean enableShutterSound(boolean enabled);
+    public final boolean enableShutterSound(boolean enabled) {
+        if (!enabled) {
+            IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+            IAudioService audioService = IAudioService.Stub.asInterface(b);
+            try {
+                if (audioService.isCameraSoundForced()) return false;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Audio service is unavailable for queries");
+            }
+        }
+        return _enableShutterSound(enabled);
+    }
+
+    private native final boolean _enableShutterSound(boolean enabled);
 
     /**
      * Callback interface for zoom changes during a smooth zoom operation.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 83a0c78..2739cac 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -16,6 +16,8 @@
 package android.os;
 
 import com.android.internal.R;
+
+import android.app.ActivityManagerNative;
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.graphics.Bitmap;
@@ -82,6 +84,39 @@
     }
  
     /**
+     * Return whether the given user is actively running.  This means that
+     * the user is in the "started" state, not "stopped" -- it is currently
+     * allowed to run code through scheduled alarms, receiving broadcasts,
+     * etc.  A started user may be either the current foreground user or a
+     * background user; the result here does not distinguish between the two.
+     * @param user The user to retrieve the running state for.
+     */
+    public boolean isUserRunning(UserHandle user) {
+        try {
+            return ActivityManagerNative.getDefault().isUserRunning(
+                    user.getIdentifier(), false);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Return whether the given user is actively running <em>or</em> stopping.
+     * This is like {@link #isUserRunning(UserHandle)}, but will also return
+     * true if the user had been running but is in the process of being stopped
+     * (but is not yet fully stopped, and still running some code).
+     * @param user The user to retrieve the running state for.
+     */
+    public boolean isUserRunningOrStopping(UserHandle user) {
+        try {
+            return ActivityManagerNative.getDefault().isUserRunning(
+                    user.getIdentifier(), true);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
      * Returns the UserInfo object describing a specific user.
      * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      * @param userHandle the user handle of the user whose information is being requested.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3bbdf36..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,
@@ -4052,7 +4058,20 @@
          * @return true if the provider is enabled
          */
         public static final boolean isLocationProviderEnabled(ContentResolver cr, String provider) {
-            String allowedProviders = Settings.Secure.getString(cr, LOCATION_PROVIDERS_ALLOWED);
+            return isLocationProviderEnabledForUser(cr, provider, UserHandle.myUserId());
+        }
+
+        /**
+         * Helper method for determining if a location provider is enabled.
+         * @param cr the content resolver to use
+         * @param provider the location provider to query
+         * @param userId the userId to query
+         * @return true if the provider is enabled
+         * @hide
+         */
+        public static final boolean isLocationProviderEnabledForUser(ContentResolver cr, String provider, int userId) {
+            String allowedProviders = Settings.Secure.getStringForUser(cr,
+                    LOCATION_PROVIDERS_ALLOWED, userId);
             return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
         }
 
@@ -4064,6 +4083,19 @@
          */
         public static final void setLocationProviderEnabled(ContentResolver cr,
                 String provider, boolean enabled) {
+            setLocationProviderEnabledForUser(cr, provider, enabled, UserHandle.myUserId());
+        }
+
+        /**
+         * Thread-safe method for enabling or disabling a single location provider.
+         * @param cr the content resolver to use
+         * @param provider the location provider to enable or disable
+         * @param enabled true if the provider should be enabled
+         * @param userId the userId for which to enable/disable providers
+         * @hide
+         */
+        public static final void setLocationProviderEnabledForUser(ContentResolver cr,
+                String provider, boolean enabled, int userId) {
             // to ensure thread safety, we write the provider name with a '+' or '-'
             // and let the SettingsProvider handle it rather than reading and modifying
             // the list of enabled providers.
@@ -4072,7 +4104,8 @@
             } else {
                 provider = "-" + provider;
             }
-            putString(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider);
+            putStringForUser(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider,
+                    userId);
         }
     }
 
@@ -5294,6 +5327,7 @@
             ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED,
             WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
             WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
+            WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED,
             WIFI_NUM_OPEN_NETWORKS_KEPT,
             EMERGENCY_TONE,
             CALL_AUTO_RETRY,
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index cb78763a..f865455 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -120,7 +120,7 @@
     private boolean mInteractive = false;
     private boolean mLowProfile = true;
     private boolean mFullscreen = false;
-    private boolean mScreenBright = false;
+    private boolean mScreenBright = true;
     private boolean mFinished;
 
     private boolean mDebug = false;
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index a74e438..ee3f5d8 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.os.SystemClock;
 import android.util.FloatMath;
 
@@ -162,9 +163,11 @@
         mContext = context;
         mListener = listener;
         mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2;
-        mTouchMinMajor =
-                (int) (context.getResources().getDisplayMetrics().density * TOUCH_MIN_MAJOR + 0.5f);
-        mMinSpan = context.getResources().getDimensionPixelSize(
+
+        final Resources res = context.getResources();
+        mTouchMinMajor = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.config_minScalingTouchMajor);
+        mMinSpan = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.config_minScalingSpan);
     }
 
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 8f4626f..07bb8f9 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -266,6 +266,8 @@
             IBinder displayToken, int orientation, Rect layerStackRect, Rect displayRect);
     private static native boolean nativeGetDisplayInfo(
             IBinder displayToken, PhysicalDisplayInfo outInfo);
+    private static native void nativeBlankDisplay(IBinder displayToken);
+    private static native void nativeUnblankDisplay(IBinder displayToken);
 
     private native void nativeCopyFrom(Surface other);
     private native void nativeTransferFrom(Surface other);
@@ -638,6 +640,22 @@
         return nativeGetDisplayInfo(displayToken, outInfo);
     }
 
+    /** @hide */
+    public static void blankDisplay(IBinder displayToken) {
+        if (displayToken == null) {
+            throw new IllegalArgumentException("displayToken must not be null");
+        }
+        nativeBlankDisplay(displayToken);
+    }
+
+    /** @hide */
+    public static void unblankDisplay(IBinder displayToken) {
+        if (displayToken == null) {
+            throw new IllegalArgumentException("displayToken must not be null");
+        }
+        nativeUnblankDisplay(displayToken);
+    }
+
     /**
      * Copy another surface to this one.  This surface now holds a reference
      * to the same data as the original surface, and is -not- the owner.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ae51c1d..0d76eac 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6858,12 +6858,12 @@
     /**
      * Performs the specified accessibility action on the view. For
      * possible accessibility actions look at {@link AccessibilityNodeInfo}.
-    * <p>
-    * If an {@link AccessibilityDelegate} has been specified via calling
-    * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its
-    * {@link AccessibilityDelegate#performAccessibilityAction(View, int, Bundle)}
-    * is responsible for handling this call.
-    * </p>
+     * <p>
+     * If an {@link AccessibilityDelegate} has been specified via calling
+     * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its
+     * {@link AccessibilityDelegate#performAccessibilityAction(View, int, Bundle)}
+     * is responsible for handling this call.
+     * </p>
      *
      * @param action The action to perform.
      * @param arguments Optional action arguments.
@@ -6886,12 +6886,14 @@
         switch (action) {
             case AccessibilityNodeInfo.ACTION_CLICK: {
                 if (isClickable()) {
-                    return performClick();
+                    performClick();
+                    return true;
                 }
             } break;
             case AccessibilityNodeInfo.ACTION_LONG_CLICK: {
                 if (isLongClickable()) {
-                    return performLongClick();
+                    performLongClick();
+                    return true;
                 }
             } break;
             case AccessibilityNodeInfo.ACTION_FOCUS: {
diff --git a/core/java/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_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 99d49ec..67d831c 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -854,7 +854,7 @@
   { "getNumberOfCameras",
     "()I",
     (void *)android_hardware_Camera_getNumberOfCameras },
-  { "getCameraInfo",
+  { "_getCameraInfo",
     "(ILandroid/hardware/Camera$CameraInfo;)V",
     (void*)android_hardware_Camera_getCameraInfo },
   { "native_setup",
@@ -917,7 +917,7 @@
   { "setDisplayOrientation",
     "(I)V",
     (void *)android_hardware_Camera_setDisplayOrientation },
-  { "enableShutterSound",
+  { "_enableShutterSound",
     "(Z)Z",
     (void *)android_hardware_Camera_enableShutterSound },
   { "_startFaceDetection",
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index fc04cd1..4982f31 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -48,6 +48,7 @@
 #include <android_runtime/android_view_SurfaceSession.h>
 #include <android_runtime/android_graphics_SurfaceTexture.h>
 #include <utils/misc.h>
+#include <utils/Log.h>
 
 #include <ScopedUtfChars.h>
 
@@ -710,6 +711,22 @@
     return JNI_TRUE;
 }
 
+static void nativeBlankDisplay(JNIEnv* env, jclass clazz, jobject tokenObj) {
+    sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+    if (token == NULL) return;
+
+    ALOGD_IF_SLOW(100, "Excessive delay in blankDisplay() while turning screen off");
+    SurfaceComposerClient::blankDisplay(token);
+}
+
+static void nativeUnblankDisplay(JNIEnv* env, jclass clazz, jobject tokenObj) {
+    sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+    if (token == NULL) return;
+
+    ALOGD_IF_SLOW(100, "Excessive delay in unblankDisplay() while turning screen on");
+    SurfaceComposerClient::unblankDisplay(token);
+}
+
 // ----------------------------------------------------------------------------
 
 static void nativeCopyFrom(JNIEnv* env, jobject surfaceObj, jobject otherObj) {
@@ -832,6 +849,10 @@
             (void*)nativeSetDisplayProjection },
     {"nativeGetDisplayInfo", "(Landroid/os/IBinder;Landroid/view/Surface$PhysicalDisplayInfo;)Z",
             (void*)nativeGetDisplayInfo },
+    {"nativeBlankDisplay", "(Landroid/os/IBinder;)V",
+            (void*)nativeBlankDisplay },
+    {"nativeUnblankDisplay", "(Landroid/os/IBinder;)V",
+            (void*)nativeUnblankDisplay },
     {"nativeCopyFrom", "(Landroid/view/Surface;)V",
             (void*)nativeCopyFrom },
     {"nativeTransferFrom", "(Landroid/view/Surface;)V",
diff --git a/core/res/res/values-mcc440/config.xml b/core/res/res/values-mcc440/config.xml
new file mode 100644
index 0000000..4ca1677
--- /dev/null
+++ b/core/res/res/values-mcc440/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- Whether camera shutter sound is forced or not  (country specific). -->
+    <bool name="config_camera_sound_forced">true</bool>
+
+</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 16960c8..4698002 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -941,6 +941,10 @@
          reported by the hardware. -->
     <dimen name="config_minScalingSpan">27mm</dimen>
 
+    <!-- Minimum accepted value for touchMajor while scaling. This may be tuned
+         per-device in overlays. -->
+    <dimen name="config_minScalingTouchMajor">48dp</dimen>
+
     <!-- Safe headphone volume index. When music stream volume is below this index
     the SPL on headphone output is compliant to EN 60950 requirements for portable music
     players. -->
@@ -981,4 +985,12 @@
     -->
     <bool name="config_wifiDisplaySupportsProtectedBuffers">false</bool>
 
+    <!-- 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 281d92a..c48de1f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -275,6 +275,8 @@
   <java-symbol type="bool" name="config_enableWifiDisplay" />
   <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" />
@@ -1149,6 +1151,7 @@
   <java-symbol type="string" name="bluetooth_a2dp_audio_route_name" />
 
   <java-symbol type="dimen" name="config_minScalingSpan" />
+  <java-symbol type="dimen" name="config_minScalingTouchMajor" />
 
   <!-- From android.policy -->
   <java-symbol type="anim" name="app_starting_exit" />
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/about/monetizing.jd b/docs/html/distribute/googleplay/about/monetizing.jd
index d5c6dfa..47d5266 100644
--- a/docs/html/distribute/googleplay/about/monetizing.jd
+++ b/docs/html/distribute/googleplay/about/monetizing.jd
@@ -76,7 +76,7 @@
 <p>The payment methods available to users worldwide may vary, based on
 location, carrier network, and other factors.</p>
 
-<div style="float:left;margin-right:2em;margin-top:1em;width:220px;">
+<div style="float:left;margin-right:2em;margin-top:3em;margin-bottom:1em;width:220px;">
 <img src="{@docRoot}images/gp-subs.png" style="width:220px">
 </div>
 
@@ -97,7 +97,7 @@
 <ul>
 <li>Free (no charge to download)</li>
 <li>Priced (user charged before download)</li>
-<li>In-App products and subscriptions</li>
+<li>In-app products and subscriptions</li>
 </ul>
 </div>
 </div>
@@ -111,6 +111,9 @@
 gameplay levels, and upgrades as in-app products. The only restriction is that
 free apps must remain free (to download) for the life of the app.</p>
 
+<p>For details about in-app products or subscriptions,
+see <a href="/guide/google/play/billing/index.html">Google Play In-app Billing</a>.</p>
+
 <h2 id="buyer-currency" style="margin-top:1.5em;">Flexible pricing in the currencies of your customers</h2>
 
 <div style="float:right;margin-left:18px;border:1px solid #DDD;">
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/docs/html/guide/google/play/billing/billing_subscriptions.jd b/docs/html/guide/google/play/billing/billing_subscriptions.jd
index ae12951..68eda196 100755
--- a/docs/html/guide/google/play/billing/billing_subscriptions.jd
+++ b/docs/html/guide/google/play/billing/billing_subscriptions.jd
@@ -12,6 +12,7 @@
         <li><a href="#publishing">Subscription publishing and unpublishing</a></li>
         <li><a href="#pricing">Subscription pricing</a></li>
         <li><a href="#user-billing">User billing</a></li>
+        <li><a href="#trials">Free trial period</a></li>
         <li><a href="#cancellation">Subscription cancellation</a></li>
         <li><a href="#uninstallation">App uninstallation</a></li>
         <li><a href="#refunds">Refunds</a></li>
@@ -94,8 +95,10 @@
 <p>As with other in-app products, you configure and publish subscriptions using
 the Developer Console and then sell them from inside apps installed on an
 Android-powered devices. In the Developer console, you create subscription
-products and add them to a product list, setting a price for each, choosing a
-billing interval of monthly or annually, and then publishing. In your apps, it’s
+products and add them to a product list, then set a price and optional trial
+period for each, choose a billing interval (monthly or annual), and then publish.</p>
+
+<p>In your apps, it’s
 straightforward to add support for subscription purchases. The implementation
 extends the standard In-app Billing API to support a new product type but uses
 the same communication model, data structures, and user interactions as for
@@ -145,6 +148,7 @@
     <li>You can set up subscriptions with either monthly or annual billing</li>
     <li>You can sell multiple subscription items in an app with various billing
     intervals or prices, such as for promotions</li>
+    <li>You can offer a configurable trial period for any subscription. <span class="new" style="font-size:.78em;">New!</span></li>
     <li>Users purchase your subscriptions from inside your apps, rather than
     directly from Google Play</li>
     <li>Users manage their purchased subscriptions from the My Apps screen in
@@ -251,6 +255,41 @@
 errors that may occur. Your backend servers can use the server-side API to query
 and update your records and follow up with customers directly, if needed.</p>
 
+<h3 id="trials">Free Trial Period</h3>
+
+<p>For any subscription, you can set up a free trial period that lets users
+try your subscription content before buying it. The trial period
+runs for the period of time that you set and then automatically converts to a full subscription
+managed according to the subscription's billing interval and price.</p>
+
+<p>To take advantage of a free trial, a user must "purchase" the full
+subscription through the standard In-app Billing flow, providing a valid form of
+payment to use for billing and completing the normal purchase transaction.
+However, the user is not charged any money, since the initial period corresponds
+to the free trial. Instead, Google Play records a transaction of $0.00 and the
+subscription is marked as purchased for the duration of the trial period or
+until cancellation. When the transaction is complete, Google Play notifies users
+by email that they have purchased a subscription that includes a free trial
+period and that the initial charge was $0.00. </p>
+
+<p>When the trial period ends, Google Play automatically initiates billing
+against the credit card that the user provided during the initial purchase, at the amount set
+for the full subscription, and continuing at the subscription interval. If
+necessary, the user can cancel the subscription at any time during the trial
+period. In this case, Google Play <em>marks the subscription as expired immediately</em>,
+rather than waiting until the end of the trial period. The user has not
+paid for the trial period and so is not entitled to continued access after
+cancellation.</p>
+
+<p>You can set up a trial period for a subscription in the Developer Console,
+without needing to modify or update your APK. Just locate and edit the
+subscription in your product list, set a valid number of days for the trial
+(must be 7 days or longer), and publish. You can change the period any time,
+although note that Google Play does not apply the change to users who have
+already "purchased" a trial period for the subscription. Only new subscription
+purchases will use the updated trial period. You can create one free trial
+period per subscription product.</p>
+
 <h3 id="cancellation">Subscription cancellation</h3>
 
 <p>Users can view the status of all of their subscriptions and cancel them if
diff --git a/docs/html/guide/google/play/billing/index.jd b/docs/html/guide/google/play/billing/index.jd
index a33b199..134140d 100755
--- a/docs/html/guide/google/play/billing/index.jd
+++ b/docs/html/guide/google/play/billing/index.jd
@@ -42,10 +42,8 @@
 
 <div class="sidebox-wrapper">
 <div class="sidebox">
-  <h2>Support for subscriptions <span class="new">New!</span></h2>
-  <p>In-app Billing now lets you sell subscriptions in your apps, as well as standard in-app products. 
-  For details on how to sell subscriptions to content, services, and features, see the
-  <a href="{@docRoot}guide/google/play/billing/billing_subscriptions.html">Subscriptions</a> documentation.</p>
+  <p><strong>Free trials for subscriptions</strong> <span class="new" style="font-size:.78em;">New!</span></p>
+  <p>You can now offer users a configurable <a href="{@docRoot}guide/google/play/billing/billing_subscriptions.html#trials">free trial period</a> for your in-app subscriptions. You can set up trials with a simple change in the Developer Console&mdash;no change to your app code is needed. 
 </div>
 </div>
 
diff --git a/docs/html/guide/topics/connectivity/nfc/nfc.jd b/docs/html/guide/topics/connectivity/nfc/nfc.jd
index 51c7bee..5011872 100644
--- a/docs/html/guide/topics/connectivity/nfc/nfc.jd
+++ b/docs/html/guide/topics/connectivity/nfc/nfc.jd
@@ -318,8 +318,8 @@
 </pre>
     </li>
 
-    <li>The <code>uses-feature</code> element so that your application shows up in Google
-Play only for devices that have NFC hardware:
+    <li>The <code>uses-feature</code> element so that your application shows up in Google Play
+    only for devices that have NFC hardware:
       <pre>
 &lt;uses-feature android:name="android.hardware.nfc" android:required="true" /&gt;
 </pre>
@@ -511,13 +511,24 @@
 
 <h2 id="creating-records">Creating Common Types of NDEF Records</h2>
 <p>This section describes how to create common types of NDEF records to help you when writing to
-NFC tags or sending data with Android Beam. It also describes how to create the corresponding
+NFC tags or sending data with Android Beam. Starting with Android 4.0 (API level 14), the
+{@link android.nfc.NdefRecord#createUri createUri()} method is available to help you create
+URI records automatically. Starting in Android 4.1 (API level 16), {@link android.nfc.NdefRecord#createExternal createExternal()}
+and {@link android.nfc.NdefRecord#createMime createMime()} are available to help you create
+MIME and external type NDEF records. Use these helper methods whenever possible to avoid mistakes
+when manually creating NDEF records.</p>
+
+<p>
+This section also describes how to create the corresponding
 intent filter for the record. All of these NDEF record examples should be in the first NDEF
 record of the NDEF message that you are writing to a tag or beaming.</p>
 
 <h3 id="abs-uri">TNF_ABSOLUTE_URI</h3>
-<p>Given the following {@link android.nfc.NdefRecord#TNF_ABSOLUTE_URI} NDEF record, which is
-stored as the first record inside of an {@link android.nfc.NdefMessage}:</p>
+<p class="note"><strong>Note:</strong> We recommend that you use the
+  <a href="#well-known-uri"><code>RTD_URI</code></a> type instead
+  of {@link android.nfc.NdefRecord#TNF_ABSOLUTE_URI}, because it is more efficient.</p>
+
+<p>You can create a {@link android.nfc.NdefRecord#TNF_ABSOLUTE_URI} NDEF record in the following way:</p>
 
 <pre>
 NdefRecord uriRecord = new NdefRecord(
@@ -526,7 +537,7 @@
     new byte[0], new byte[0]);
 </pre>
 
-<p>the intent filter would look like this:</p>
+<p>The intent filter for the previous NDEF record would look like this:</p>
 <pre>
 &lt;intent-filter&gt;
     &lt;action android:name="android.nfc.action.NDEF_DISCOVERED" /&gt;
@@ -537,32 +548,35 @@
 &lt;/intent-filter&gt;
 </pre>
 
-
 <h3 id="mime">TNF_MIME_MEDIA</h3>
-<p>Given the following {@link android.nfc.NdefRecord#TNF_MIME_MEDIA} NDEF record, which is stored as
-the first record inside
-of an {@link android.nfc.NdefMessage}:</p>
+<p>You can create a {@link android.nfc.NdefRecord#TNF_MIME_MEDIA} NDEF record in the following ways.</p>
+
+<p>Using the {@link android.nfc.NdefRecord#createMime createMime()} method:</p>
+<pre>
+NdefRecord mimeRecord = NdefRecord.createMime("application/vnd.com.example.android.beam",
+    "Beam me up, Android".getBytes(Charset.forName("US-ASCII")));
+</pre>
+
+<p>Creating the {@link android.nfc.NdefRecord} manually:</p>
 <pre>
 NdefRecord mimeRecord = new NdefRecord(
     NdefRecord.TNF_MIME_MEDIA ,
-    "application/com.example.android.beam".getBytes(Charset.forName("US-ASCII")),
+    "application/vnd.com.example.android.beam".getBytes(Charset.forName("US-ASCII")),
     new byte[0], "Beam me up, Android!".getBytes(Charset.forName("US-ASCII")));
 </pre>
 
-<p>the intent filter would look like this:</p>
+<p>The intent filter for the previous NDEF records would look like this:</p>
 <pre>
 &lt;intent-filter&gt;
     &lt;action android:name="android.nfc.action.NDEF_DISCOVERED" /&gt;
     &lt;category android:name="android.intent.category.DEFAULT" /&gt;
-    &lt;data android:mimeType="application/com.example.android.beam" /&gt;
+    &lt;data android:mimeType="application/vnd.com.example.android.beam" /&gt;
 &lt;/intent-filter&gt;
 </pre>
 
-
 <h3 id="well-known-text">TNF_WELL_KNOWN with RTD_TEXT</h3>
 
-<p>Given the following {@link android.nfc.NdefRecord#TNF_WELL_KNOWN} NDEF record, which is stored as
-the first record inside of an {@link android.nfc.NdefMessage}:</p>
+<p>You can create a {@link android.nfc.NdefRecord#TNF_WELL_KNOWN} NDEF record in the following way:</p>
 <pre>
 public NdefRecord createTextRecord(String payload, Locale locale, boolean encodeInUtf8) {
     byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII"));
@@ -592,9 +606,20 @@
 
 <h3 id="well-known-uri">TNF_WELL_KNOWN with RTD_URI</h3>
 
-<p>Given the following {@link android.nfc.NdefRecord#TNF_WELL_KNOWN} NDEF record, which is stored as
-the first record inside of an {@link android.nfc.NdefMessage}:</p>
+<p>You can create a {@link android.nfc.NdefRecord#TNF_WELL_KNOWN} NDEF record in the following ways.</p>
 
+<p>Using the {@link android.nfc.NdefRecord#createUri(String)} method:</p>
+<pre>
+NdefRecord rtdUriRecord1 = NdefRecord.createUri("http://example.com");
+</pre>
+
+<p>Using the {@link android.nfc.NdefRecord#createUri(Uri)} method:</p>
+<pre>
+Uri uri = new Uri("http://example.com");
+NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri);
+</pre>
+
+<p>Creating the {@link android.nfc.NdefRecord} manually:</p>
 <pre>
 byte[] uriField = "example.com".getBytes(Charset.forName("US-ASCII"));
 byte[] payload = new byte[uriField.length + 1];              //add 1 for the URI Prefix
@@ -604,7 +629,7 @@
     NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], payload);
 </pre>
 
-<p>the intent filter would look like this:</p>
+<p>The intent filter for the previous NDEF records would look like this:</p>
 
 <pre>
 &lt;intent-filter&gt;
@@ -617,24 +642,32 @@
 </pre>
 
 <h3 id="ext-type">TNF_EXTERNAL_TYPE</h3>
-<p>Given the following {@link android.nfc.NdefRecord#TNF_EXTERNAL_TYPE} NDEF record, which is stored
-as the first record inside of an {@link android.nfc.NdefMessage}:</p>
+<p>You can create a {@link android.nfc.NdefRecord#TNF_EXTERNAL_TYPE} NDEF record in the following ways:</p>
 
+<p>Using the {@link android.nfc.NdefRecord#createExternal createExternal()} method:
+<pre>
+byte[] payload; //assign to your data
+String domain = "com.example"; //usually your app's package name
+String type = "externalType";
+NdefRecord extRecord = NdefRecord.createExternal(domain, type, payload);
+</pre>
+
+<p>Creating the {@link android.nfc.NdefRecord} manually:</p>
 <pre>
 byte[] payload;
 ...
-NdefRecord mimeRecord = new NdefRecord(
-    NdefRecord.TNF_EXTERNAL_TYPE, "example.com:externalType", new byte[0], payload);
+NdefRecord extRecord = new NdefRecord(
+    NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType", new byte[0], payload);
 </pre>
 
-<p>the intent filter would look like this:</p>
+<p>The intent filter for the previous NDEF records would look like this:</p>
 <pre>
 &lt;intent-filter&gt;
     &lt;action android:name="android.nfc.action.NDEF_DISCOVERED" /&gt;
     &lt;category android:name="android.intent.category.DEFAULT" /&gt;
     &lt;data android:scheme="vnd.android.nfc"
         android:host="ext"
-        android:pathPrefix="/example.com:externalType"/&gt;
+        android:pathPrefix="/com.example:externalType"/&gt;
 &lt;/intent-filter&gt;
 </pre>
 
@@ -840,8 +873,8 @@
         String text = ("Beam me up, Android!\n\n" +
                 "Beam Time: " + System.currentTimeMillis());
         NdefMessage msg = new NdefMessage(
-                new NdefRecord[] { createMimeRecord(
-                        "application/com.example.android.beam", text.getBytes())
+                new NdefRecord[] { createMime(
+                        "application/vnd.com.example.android.beam", text.getBytes())
          /**
           * The Android Application Record (AAR) is commented out. When a device
           * receives a push with an AAR in it, the application specified in the AAR
@@ -882,22 +915,12 @@
         // record 0 contains the MIME type, record 1 is the AAR, if present
         textView.setText(new String(msg.getRecords()[0].getPayload()));
     }
-
-    /**
-     * Creates a custom MIME type encapsulated in an NDEF record
-     */
-    public NdefRecord createMimeRecord(String mimeType, byte[] payload) {
-        byte[] mimeBytes = mimeType.getBytes(Charset.forName("US-ASCII"));
-        NdefRecord mimeRecord = new NdefRecord(
-                NdefRecord.TNF_MIME_MEDIA, mimeBytes, new byte[0], payload);
-        return mimeRecord;
-    }
 }
 </pre>
 
 <p>Note that this code comments out an AAR, which you can remove. If you enable the AAR, the
 application specified in the AAR always receives the Android Beam message. If the application is not
-present, Google Play launches to download the application. Therefore, the following intent
+present, Google Play is started to download the application. Therefore, the following intent
 filter is not technically necessary for Android 4.0 devices or later if the AAR is used:
 </p>
 
@@ -905,13 +928,13 @@
 &lt;intent-filter&gt;
   &lt;action android:name="android.nfc.action.NDEF_DISCOVERED"/&gt;
   &lt;category android:name="android.intent.category.DEFAULT"/&gt;
-  &lt;data android:mimeType="application/com.example.android.beam"/&gt;
+  &lt;data android:mimeType="application/vnd.com.example.android.beam"/&gt;
 &lt;/intent-filter&gt;
 </pre>
 <p>With this intent filter, the <code>com.example.android.beam</code> application now can be started
 when it scans an NFC tag or receives an Android Beam with an AAR of
 type <code>com.example.android.beam</code>, or when an NDEF formatted message contains a MIME record
-of type <code>application/com.example.android.beam</code>.</p>
+of type <code>application/vnd.com.example.android.beam</code>.</p>
 
 <p>Even though AARs guarantee an application is started or downloaded, intent filters are
 recommended, because they let you start an Activity of your choice in your
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 6ba57809..4604437 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -413,6 +413,11 @@
         }
 
         nativeCopyPixelsFromBuffer(mNativeBitmap, src);
+
+        // now update the buffer's position
+        int position = src.position();
+        position += bitmapBytes >> shift;
+        src.position(position);
     }
 
     /**
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 7d17391..f26d322 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -462,7 +462,21 @@
         mVolumePanel = new VolumePanel(context, this);
         mMode = AudioSystem.MODE_NORMAL;
         mForcedUseForComm = AudioSystem.FORCE_NONE;
+
         createAudioSystemThread();
+
+        boolean cameraSoundForced = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_camera_sound_forced);
+        mCameraSoundForced = new Boolean(cameraSoundForced);
+        sendMsg(mAudioHandler,
+                MSG_SET_FORCE_USE,
+                SENDMSG_QUEUE,
+                AudioSystem.FOR_SYSTEM,
+                cameraSoundForced ?
+                        AudioSystem.FORCE_SYSTEM_ENFORCED : AudioSystem.FORCE_NONE,
+                null,
+                0);
+
         readPersistedSettings();
         mSettingsObserver = new SettingsObserver();
         updateStreamVolumeAlias(false /*updateVolumes*/);
@@ -585,6 +599,8 @@
             mStreamStates[i].dump(pw);
             pw.println("");
         }
+        pw.print("\n- mute affected streams = 0x");
+        pw.println(Integer.toHexString(mMuteAffectedStreams));
     }
 
 
@@ -634,35 +650,44 @@
         }
         synchronized(mSettingsLock) {
             mRingerMode = ringerMode;
-        }
 
-        // System.VIBRATE_ON is not used any more but defaults for mVibrateSetting
-        // are still needed while setVibrateSetting() and getVibrateSetting() are being deprecated.
-        mVibrateSetting = getValueForVibrateSetting(0,
-                                        AudioManager.VIBRATE_TYPE_NOTIFICATION,
-                                        mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
-                                                        : AudioManager.VIBRATE_SETTING_OFF);
-        mVibrateSetting = getValueForVibrateSetting(mVibrateSetting,
-                                        AudioManager.VIBRATE_TYPE_RINGER,
-                                        mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
-                                                        : AudioManager.VIBRATE_SETTING_OFF);
+            // System.VIBRATE_ON is not used any more but defaults for mVibrateSetting
+            // are still needed while setVibrateSetting() and getVibrateSetting() are being
+            // deprecated.
+            mVibrateSetting = getValueForVibrateSetting(0,
+                                            AudioManager.VIBRATE_TYPE_NOTIFICATION,
+                                            mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
+                                                            : AudioManager.VIBRATE_SETTING_OFF);
+            mVibrateSetting = getValueForVibrateSetting(mVibrateSetting,
+                                            AudioManager.VIBRATE_TYPE_RINGER,
+                                            mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
+                                                            : AudioManager.VIBRATE_SETTING_OFF);
 
-        // make sure settings for ringer mode are consistent with device type: non voice capable
-        // devices (tablets) include media stream in silent mode whereas phones don't.
-        mRingerModeAffectedStreams = Settings.System.getIntForUser(cr,
-                Settings.System.MODE_RINGER_STREAMS_AFFECTED,
-                ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
-                 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)),
-                 UserHandle.USER_CURRENT);
-        if (mVoiceCapable) {
-            mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
-        } else {
-            mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
+            // make sure settings for ringer mode are consistent with device type: non voice capable
+            // devices (tablets) include media stream in silent mode whereas phones don't.
+            mRingerModeAffectedStreams = Settings.System.getIntForUser(cr,
+                    Settings.System.MODE_RINGER_STREAMS_AFFECTED,
+                    ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
+                     (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)),
+                     UserHandle.USER_CURRENT);
+            if (mVoiceCapable) {
+                mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
+            } else {
+                mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
+            }
+            synchronized (mCameraSoundForced) {
+                if (mCameraSoundForced) {
+                    mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+                } else {
+                    mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+                }
+            }
+
+            Settings.System.putIntForUser(cr,
+                    Settings.System.MODE_RINGER_STREAMS_AFFECTED,
+                    mRingerModeAffectedStreams,
+                    UserHandle.USER_CURRENT);
         }
-        Settings.System.putIntForUser(cr,
-                Settings.System.MODE_RINGER_STREAMS_AFFECTED,
-                mRingerModeAffectedStreams,
-                UserHandle.USER_CURRENT);
 
         mMuteAffectedStreams = System.getIntForUser(cr,
                 System.MUTE_STREAMS_AFFECTED,
@@ -2601,12 +2626,18 @@
             // only be stale values
             // on first call to readSettings() at init time, muteCount() is always 0 so we will
             // always create entries for default device
-            if ((muteCount() == 0) && (mStreamType == AudioSystem.STREAM_SYSTEM) ||
+            if ((mStreamType == AudioSystem.STREAM_SYSTEM) ||
                     (mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED)) {
-                mLastAudibleIndex.put(AudioSystem.DEVICE_OUT_DEFAULT,
-                                          10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]);
-                mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT,
-                               10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]);
+                int index = 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
+                synchronized (mCameraSoundForced) {
+                    if (mCameraSoundForced) {
+                        index = mIndexMax;
+                    }
+                }
+                if (muteCount() == 0) {
+                    mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
+                }
+                mLastAudibleIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
                 return;
             }
 
@@ -2618,10 +2649,11 @@
                 remainingDevices &= ~device;
 
                 // ignore settings for fixed volume devices: volume should always be at max
-                if ((muteCount() == 0) &&
-                        (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) &&
+                if ((mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) &&
                         ((device & mFixedVolumeDevices) != 0)) {
-                    mIndex.put(device, mIndexMax);
+                    if (muteCount() == 0) {
+                        mIndex.put(device, mIndexMax);
+                    }
                     mLastAudibleIndex.put(device, mIndexMax);
                     continue;
                 }
@@ -2676,7 +2708,9 @@
                             this,
                             PERSIST_DELAY);
                 }
-                mIndex.put(device, getValidIndex(10 * index));
+                if (muteCount() == 0) {
+                    mIndex.put(device, getValidIndex(10 * index));
+                }
             }
         }
 
@@ -2716,6 +2750,11 @@
         public synchronized boolean setIndex(int index, int device, boolean lastAudible) {
             int oldIndex = getIndex(device, false  /* lastAudible */);
             index = getValidIndex(index);
+            synchronized (mCameraSoundForced) {
+                if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {
+                    index = mIndexMax;
+                }
+            }
             mIndex.put(device, getValidIndex(index));
 
             if (oldIndex != index) {
@@ -2819,6 +2858,21 @@
             }
         }
 
+        public synchronized void setAllIndexesToMax() {
+            Set set = mIndex.entrySet();
+            Iterator i = set.iterator();
+            while (i.hasNext()) {
+                Map.Entry entry = (Map.Entry)i.next();
+                entry.setValue(mIndexMax);
+            }
+            set = mLastAudibleIndex.entrySet();
+            i = set.iterator();
+            while (i.hasNext()) {
+                Map.Entry entry = (Map.Entry)i.next();
+                entry.setValue(mIndexMax);
+            }
+        }
+
         public synchronized void mute(IBinder cb, boolean state) {
             VolumeDeathHandler handler = getDeathHandler(cb, state);
             if (handler == null) {
@@ -2967,6 +3021,8 @@
         }
 
         private void dump(PrintWriter pw) {
+            pw.print("   Mute count: ");
+            pw.println(muteCount());
             pw.print("   Current: ");
             Set set = mIndex.entrySet();
             Iterator i = set.iterator();
@@ -3215,6 +3271,8 @@
                     // Restore forced usage for communcations and record
                     AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
                     AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
+                    AudioSystem.setForceUse(AudioSystem.FOR_SYSTEM, mCameraSoundForced ?
+                                    AudioSystem.FORCE_SYSTEM_ENFORCED : AudioSystem.FORCE_NONE);
 
                     // Restore stream volumes
                     int numStreamTypes = AudioSystem.getNumStreamTypes();
@@ -3372,6 +3430,13 @@
                 } else {
                     ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
                 }
+                synchronized (mCameraSoundForced) {
+                    if (mCameraSoundForced) {
+                        ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+                    } else {
+                        ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+                    }
+                }
                 if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
                     /*
                      * Ensure all stream types that should be affected by ringer mode
@@ -5587,6 +5652,48 @@
                     0,
                     null,
                     0);
+
+            boolean cameraSoundForced = mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_camera_sound_forced);
+            synchronized (mSettingsLock) {
+                synchronized (mCameraSoundForced) {
+                    if (cameraSoundForced != mCameraSoundForced) {
+                        mCameraSoundForced = cameraSoundForced;
+
+                        VolumeStreamState s = mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED];
+                        if (cameraSoundForced) {
+                            s.setAllIndexesToMax();
+                            mRingerModeAffectedStreams &=
+                                    ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+                        } else {
+                            s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM],
+                                            false /*lastAudible*/);
+                            s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM],
+                                            true /*lastAudible*/);
+                            mRingerModeAffectedStreams |=
+                                    (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+                        }
+                        // take new state into account for streams muted by ringer mode
+                        setRingerModeInt(getRingerMode(), false);
+
+                        sendMsg(mAudioHandler,
+                                MSG_SET_FORCE_USE,
+                                SENDMSG_QUEUE,
+                                AudioSystem.FOR_SYSTEM,
+                                cameraSoundForced ?
+                                        AudioSystem.FORCE_SYSTEM_ENFORCED : AudioSystem.FORCE_NONE,
+                                null,
+                                0);
+
+                        sendMsg(mAudioHandler,
+                                MSG_SET_ALL_VOLUMES,
+                                SENDMSG_QUEUE,
+                                0,
+                                0,
+                                mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED], 0);
+                    }
+                }
+            }
         } catch (Exception e) {
             Log.e(TAG, "Error retrieving device orientation: " + e);
         }
@@ -5762,6 +5869,38 @@
     }
 
 
+    //==========================================================================================
+    // Camera shutter sound policy.
+    // config_camera_sound_forced configuration option in config.xml defines if the camera shutter
+    // sound is forced (sound even if the device is in silent mode) or not. This option is false by
+    // default and can be overridden by country specific overlay in values-mccXXX/config.xml.
+    //==========================================================================================
+
+    // cached value of com.android.internal.R.bool.config_camera_sound_forced
+    private Boolean mCameraSoundForced;
+
+    // called by android.hardware.Camera to populate CameraInfo.canDisableShutterSound
+    public boolean isCameraSoundForced() {
+        synchronized (mCameraSoundForced) {
+            return mCameraSoundForced;
+        }
+    }
+
+    private static final String[] RINGER_MODE_NAMES = new String[] {
+            "SILENT",
+            "VIBRATE",
+            "NORMAL"
+    };
+
+    private void dumpRingerMode(PrintWriter pw) {
+        pw.println("\nRinger mode: ");
+        pw.println("- mode: "+RINGER_MODE_NAMES[mRingerMode]);
+        pw.print("- ringer mode affected streams = 0x");
+        pw.println(Integer.toHexString(mRingerModeAffectedStreams));
+        pw.print("- ringer mode muted streams = 0x");
+        pw.println(Integer.toHexString(mRingerModeMutedStreams));
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
@@ -5770,6 +5909,7 @@
         dumpRCStack(pw);
         dumpRCCStack(pw);
         dumpStreamStates(pw);
+        dumpRingerMode(pw);
         pw.println("\nAudio routes:");
         pw.print("  mMainType=0x"); pw.println(Integer.toHexString(mCurAudioRoutes.mMainType));
         pw.print("  mBluetoothName="); pw.println(mCurAudioRoutes.mBluetoothName);
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 2cff4ff..103e817 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -354,7 +354,8 @@
     public static final int FORCE_DIGITAL_DOCK = 9;
     public static final int FORCE_NO_BT_A2DP = 10;
     public static final int FORCE_REMOTE_SUBMIX = 11;
-    private static final int NUM_FORCE_CONFIG = 12;
+    public static final int FORCE_SYSTEM_ENFORCED = 12;
+    private static final int NUM_FORCE_CONFIG = 13;
     public static final int FORCE_DEFAULT = FORCE_NONE;
 
     // usage for setForceUse, must match AudioSystem::force_use
@@ -362,7 +363,8 @@
     public static final int FOR_MEDIA = 1;
     public static final int FOR_RECORD = 2;
     public static final int FOR_DOCK = 3;
-    private static final int NUM_FORCE_USE = 4;
+    public static final int FOR_SYSTEM = 4;
+    private static final int NUM_FORCE_USE = 5;
 
     // usage for AudioRecord.startRecordingSync(), must match AudioSystem::sync_event_t
     public static final int SYNC_EVENT_NONE = 0;
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 7ae61cd..ea99069 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -153,4 +153,6 @@
     int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state);
 
     AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer);
+
+    boolean isCameraSoundForced();
 }
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index a4516ab..16ad74f 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -693,7 +693,8 @@
         final WifiDisplayStatus oldStatus = sStatic.mLastKnownWifiDisplayStatus;
 
         // TODO Naive implementation. Make this smarter later.
-        boolean needScan = false;
+        boolean wantScan = false;
+        boolean blockScan = false;
         WifiDisplay[] oldDisplays = oldStatus != null ?
                 oldStatus.getRememberedDisplays() : new WifiDisplay[0];
         WifiDisplay[] newDisplays = newStatus.getRememberedDisplays();
@@ -706,7 +707,7 @@
             if (oldRemembered == null) {
                 addRouteStatic(makeWifiDisplayRoute(d,
                         findMatchingDisplay(d, availableDisplays) != null));
-                needScan = true;
+                wantScan = true;
             } else {
                 final boolean available = findMatchingDisplay(d, availableDisplays) != null;
                 final RouteInfo route = findWifiDisplayRoute(d);
@@ -716,6 +717,10 @@
                 final RouteInfo activeRoute = findWifiDisplayRoute(d);
                 if (activeRoute != null) {
                     selectRouteStatic(activeRoute.getSupportedTypes(), activeRoute);
+
+                    // Don't scan if we're already connected to a wifi display,
+                    // the scanning process can cause a hiccup with some configurations.
+                    blockScan = true;
                 }
             }
         }
@@ -727,7 +732,7 @@
             }
         }
 
-        if (needScan) {
+        if (wantScan && !blockScan) {
             sStatic.mDisplayService.scanWifiDisplays();
         }
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPerfTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPerfTestRunner.java
index a6cf355..3d5905d 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPerfTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPerfTestRunner.java
@@ -38,7 +38,7 @@
 public class MediaFrameworkPerfTestRunner extends InstrumentationTestRunner {
 
     public static boolean mGetNativeHeapDump = false;
-
+    public static boolean mGetProcmem = false;
 
     @Override
     public TestSuite getAllTests() {
@@ -61,6 +61,12 @@
         if (get_heap_dump != null) {
             mGetNativeHeapDump = true;
         }
+
+        String get_procmem = (String) icicle.get("get_procmem");
+        if (get_procmem != null) {
+            mGetProcmem = true;
+        }
+
     }
 }
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
index ccb0638..9b1098e 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -1,12 +1,12 @@
 /*
  * Copyright (C) 2008 The Android Open Source Project
- * 
+ *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
  * the License at
- * 
+ *
  * http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -53,7 +53,7 @@
 import com.android.mediaframeworktest.MediaProfileReader;
 
 /**
- * Junit / Instrumentation - performance measurement for media player and 
+ * Junit / Instrumentation - performance measurement for media player and
  * recorder
  *
  * FIXME:
@@ -100,6 +100,7 @@
         super("com.android.mediaframeworktest", MediaFrameworkTest.class);
     }
 
+    @Override
     protected void setUp() throws Exception {
         super.setUp();
         //Insert a 2 second before launching the test activity. This is
@@ -109,19 +110,26 @@
         if (MediaFrameworkPerfTestRunner.mGetNativeHeapDump)
             MediaTestUtil.getNativeHeapDump(this.getName() + "_before");
 
-        mProcMemWriter = new BufferedWriter(new FileWriter
-                (new File(MEDIA_PROCMEM_OUTPUT), true));
-        mProcMemWriter.write(this.getName() + "\n");
-        mMemWriter = new BufferedWriter(new FileWriter
-                (new File(MEDIA_MEMORY_OUTPUT), true));
+        if (MediaFrameworkPerfTestRunner.mGetProcmem) {
+            mProcMemWriter = new BufferedWriter(new FileWriter
+                    (new File(MEDIA_PROCMEM_OUTPUT), true));
+            mProcMemWriter.write(this.getName() + "\n");
+            mMemWriter = new BufferedWriter(new FileWriter
+                    (new File(MEDIA_MEMORY_OUTPUT), true));
+        }
 
     }
 
+    @Override
     protected void tearDown() throws Exception {
         if (MediaFrameworkPerfTestRunner.mGetNativeHeapDump)
             MediaTestUtil.getNativeHeapDump(this.getName() + "_after");
-        mProcMemWriter.close();
-        mMemWriter.close();
+
+        if (MediaFrameworkPerfTestRunner.mGetProcmem) {
+            mMemWriter.write("\n");
+            mProcMemWriter.close();
+            mMemWriter.close();
+        }
         super.tearDown();
     }
 
@@ -157,6 +165,7 @@
     }
 
     private final class RawPreviewCallback implements PreviewCallback {
+        @Override
         public void onPreviewFrame(byte[] rawData, Camera camera) {
             mPreviewDone.open();
         }
@@ -285,19 +294,21 @@
         }
     }
 
-    public void writeProcmemInfo() throws Exception{
-        String cmd = "procmem " + getMediaserverPid();
-        Process p = Runtime.getRuntime().exec(cmd);
+    public void writeProcmemInfo() throws Exception {
+        if (MediaFrameworkPerfTestRunner.mGetProcmem) {
+            String cmd = "procmem " + getMediaserverPid();
+            Process p = Runtime.getRuntime().exec(cmd);
 
-        InputStream inStream = p.getInputStream();
-        InputStreamReader inReader = new InputStreamReader(inStream);
-        BufferedReader inBuffer = new BufferedReader(inReader);
-        String s;
-        while ((s = inBuffer.readLine()) != null) {
-              mProcMemWriter.write(s);
-              mProcMemWriter.write("\n");
+            InputStream inStream = p.getInputStream();
+            InputStreamReader inReader = new InputStreamReader(inStream);
+            BufferedReader inBuffer = new BufferedReader(inReader);
+            String s;
+            while ((s = inBuffer.readLine()) != null) {
+                mProcMemWriter.write(s);
+                mProcMemWriter.write("\n");
+            }
+            mProcMemWriter.write("\n\n");
         }
-        mProcMemWriter.write("\n\n");
     }
 
     public String captureMediaserverInfo() {
@@ -368,13 +379,11 @@
         boolean memoryResult = false;
 
         mStartPid = getMediaserverPid();
-        mMemWriter.write("H263 Video Playback Only\n");
         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
             mediaStressPlayback(MediaNames.VIDEO_HIGHRES_H263);
             getMemoryWriteToLog(i);
             writeProcmemInfo();
         }
-        mMemWriter.write("\n");
         memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT);
         assertTrue("H263 playback memory test", memoryResult);
     }
@@ -385,13 +394,11 @@
         boolean memoryResult = false;
 
         mStartPid = getMediaserverPid();
-        mMemWriter.write("H264 Video Playback only\n");
         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
             mediaStressPlayback(MediaNames.VIDEO_H264_AMR);
             getMemoryWriteToLog(i);
             writeProcmemInfo();
         }
-        mMemWriter.write("\n");
         memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT);
         assertTrue("H264 playback memory test", memoryResult);
     }
@@ -402,7 +409,6 @@
         boolean memoryResult = false;
 
         mStartPid = getMediaserverPid();
-        mMemWriter.write("H263 video record only\n");
         int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H263);
         assertTrue("H263 video recording frame rate", frameRate != -1);
         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
@@ -411,7 +417,6 @@
             getMemoryWriteToLog(i);
             writeProcmemInfo();
         }
-        mMemWriter.write("\n");
         memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
         assertTrue("H263 record only memory test", memoryResult);
     }
@@ -422,7 +427,6 @@
         boolean memoryResult = false;
 
         mStartPid = getMediaserverPid();
-        mMemWriter.write("MPEG4 video record only\n");
         int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.MPEG_4_SP);
         assertTrue("MPEG4 video recording frame rate", frameRate != -1);
         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
@@ -431,7 +435,6 @@
             getMemoryWriteToLog(i);
             writeProcmemInfo();
         }
-        mMemWriter.write("\n");
         memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
         assertTrue("mpeg4 record only memory test", memoryResult);
     }
@@ -445,14 +448,12 @@
         mStartPid = getMediaserverPid();
         int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H263);
         assertTrue("H263 video recording frame rate", frameRate != -1);
-        mMemWriter.write("Audio and h263 video record\n");
         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
             assertTrue(stressVideoRecord(frameRate, 352, 288, MediaRecorder.VideoEncoder.H263,
                     MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, false));
             getMemoryWriteToLog(i);
             writeProcmemInfo();
         }
-        mMemWriter.write("\n");
         memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
         assertTrue("H263 audio video record memory test", memoryResult);
     }
@@ -463,13 +464,11 @@
         boolean memoryResult = false;
 
         mStartPid = getMediaserverPid();
-        mMemWriter.write("Audio record only\n");
         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
             stressAudioRecord(MediaNames.RECORDER_OUTPUT);
             getMemoryWriteToLog(i);
             writeProcmemInfo();
         }
-        mMemWriter.write("\n");
         memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
         assertTrue("audio record only memory test", memoryResult);
     }
@@ -480,13 +479,11 @@
         boolean memoryResult = false;
 
         mStartPid = getMediaserverPid();
-        mMemWriter.write("Camera Preview Only\n");
         for (int i = 0; i < NUM_STRESS_LOOP; i++) {
             stressCameraPreview();
             getMemoryWriteToLog(i);
             writeProcmemInfo();
         }
-        mMemWriter.write("\n");
         memoryResult = validateMemoryResult(mStartPid, mStartMemory, CAMERA_LIMIT);
         assertTrue("camera preview memory test", memoryResult);
     }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 0b61abe..0689268 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -68,7 +68,7 @@
     // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
     // is properly propagated through your change.  Not doing so will result in a loss of user
     // settings.
-    private static final int DATABASE_VERSION = 93;
+    private static final int DATABASE_VERSION = 94;
 
     private Context mContext;
     private int mUserHandle;
@@ -1438,7 +1438,7 @@
                 db.beginTransaction();
                 try {
                     // Move ringer mode from system to global settings
-                    String[] settingsToMove = { Settings.System.MODE_RINGER };
+                    String[] settingsToMove = { Settings.Global.MODE_RINGER };
                     moveSettingsToNewTable(db, TABLE_SYSTEM, TABLE_GLOBAL, settingsToMove, true);
 
                     db.setTransactionSuccessful();
@@ -1473,6 +1473,27 @@
             upgradeVersion = 93;
         }
 
+        if (upgradeVersion == 93) {
+            // Redo this step, since somehow it didn't work the first time for some users
+            if (mUserHandle == UserHandle.USER_OWNER) {
+                db.beginTransaction();
+                SQLiteStatement stmt = null;
+                try {
+                    // Migrate now-global settings
+                    String[] settingsToMove = hashsetToStringArray(SettingsProvider.sSystemGlobalKeys);
+                    moveSettingsToNewTable(db, TABLE_SYSTEM, TABLE_GLOBAL, settingsToMove, true);
+                    settingsToMove = hashsetToStringArray(SettingsProvider.sSecureGlobalKeys);
+                    moveSettingsToNewTable(db, TABLE_SECURE, TABLE_GLOBAL, settingsToMove, true);
+
+                    db.setTransactionSuccessful();
+                } finally {
+                    db.endTransaction();
+                    if (stmt != null) stmt.close();
+                }
+            }
+            upgradeVersion = 94;
+        }
+
         // *** Remember to update DATABASE_VERSION above!
 
         if (upgradeVersion != currentVersion) {
@@ -1921,10 +1942,6 @@
     }
 
     private void loadUISoundEffectsSettings(SQLiteStatement stmt) {
-        loadIntegerSetting(stmt, Settings.System.POWER_SOUNDS_ENABLED,
-            R.integer.def_power_sounds_enabled);
-        loadStringSetting(stmt, Settings.System.LOW_BATTERY_SOUND,
-            R.string.def_low_battery_sound);
         loadBooleanSetting(stmt, Settings.System.DTMF_TONE_WHEN_DIALING,
                 R.bool.def_dtmf_tones_enabled);
         loadBooleanSetting(stmt, Settings.System.SOUND_EFFECTS_ENABLED,
@@ -1932,17 +1949,6 @@
         loadBooleanSetting(stmt, Settings.System.HAPTIC_FEEDBACK_ENABLED,
                 R.bool.def_haptic_feedback);
 
-        loadIntegerSetting(stmt, Settings.System.DOCK_SOUNDS_ENABLED,
-            R.integer.def_dock_sounds_enabled);
-        loadStringSetting(stmt, Settings.System.DESK_DOCK_SOUND,
-            R.string.def_desk_dock_sound);
-        loadStringSetting(stmt, Settings.System.DESK_UNDOCK_SOUND,
-            R.string.def_desk_undock_sound);
-        loadStringSetting(stmt, Settings.System.CAR_DOCK_SOUND,
-            R.string.def_car_dock_sound);
-        loadStringSetting(stmt, Settings.System.CAR_UNDOCK_SOUND,
-            R.string.def_car_undock_sound);
-
         loadIntegerSetting(stmt, Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
             R.integer.def_lockscreen_sounds_enabled);
     }
@@ -2158,9 +2164,22 @@
 
             loadStringSetting(stmt, Settings.Global.LOCK_SOUND,
                     R.string.def_lock_sound);
-
             loadStringSetting(stmt, Settings.Global.UNLOCK_SOUND,
                     R.string.def_unlock_sound);
+            loadIntegerSetting(stmt, Settings.Global.POWER_SOUNDS_ENABLED,
+                    R.integer.def_power_sounds_enabled);
+            loadStringSetting(stmt, Settings.Global.LOW_BATTERY_SOUND,
+                    R.string.def_low_battery_sound);
+            loadIntegerSetting(stmt, Settings.Global.DOCK_SOUNDS_ENABLED,
+                    R.integer.def_dock_sounds_enabled);
+            loadStringSetting(stmt, Settings.Global.DESK_DOCK_SOUND,
+                    R.string.def_desk_dock_sound);
+            loadStringSetting(stmt, Settings.Global.DESK_UNDOCK_SOUND,
+                    R.string.def_desk_undock_sound);
+            loadStringSetting(stmt, Settings.Global.CAR_DOCK_SOUND,
+                    R.string.def_car_dock_sound);
+            loadStringSetting(stmt, Settings.Global.CAR_UNDOCK_SOUND,
+                    R.string.def_car_undock_sound);
 
             loadSetting(stmt, Settings.Global.SET_INSTALL_LOCATION, 0);
             loadSetting(stmt, Settings.Global.DEFAULT_INSTALL_LOCATION,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 0a0474c..0b85e70 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -26,6 +26,7 @@
 import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.os.FileUtils;
+import android.os.Handler;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.provider.Settings;
@@ -61,12 +62,6 @@
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_BACKUP = DEBUG || false;
 
-    /* Don't restore wifi config until we have new logic for parsing the
-     * saved wifi config and configuring the new APs without having to
-     * disable and re-enable wifi
-     */
-    private static final boolean NAIVE_WIFI_RESTORE = false;
-
     private static final String KEY_SYSTEM = "system";
     private static final String KEY_SECURE = "secure";
     private static final String KEY_GLOBAL = "global";
@@ -127,10 +122,16 @@
     // stored in the full-backup tarfile as well, so should not be changed.
     private static final String STAGE_FILE = "flattened-data";
 
+    // Delay in milliseconds between the restore operation and when we will bounce
+    // wifi in order to rewrite the supplicant config etc.
+    private static final long WIFI_BOUNCE_DELAY_MILLIS = 60 * 1000; // one minute
+
     private SettingsHelper mSettingsHelper;
     private WifiManager mWfm;
     private static String mWifiConfigFile;
 
+    WifiRestoreRunnable mWifiRestore = null;
+
     // Class for capturing a network definition from the wifi supplicant config file
     static class Network {
         String ssid = "";  // equals() and hashCode() need these to be non-null
@@ -297,6 +298,66 @@
         writeNewChecksums(stateChecksums, newState);
     }
 
+    class WifiRestoreRunnable implements Runnable {
+        private byte[] restoredSupplicantData;
+        private byte[] restoredWifiConfigFile;
+
+        void incorporateWifiSupplicant(BackupDataInput data) {
+            restoredSupplicantData = new byte[data.getDataSize()];
+            if (restoredSupplicantData.length <= 0) return;
+            try {
+                data.readEntityData(restoredSupplicantData, 0, data.getDataSize());
+            } catch (IOException e) {
+                Log.w(TAG, "Unable to read supplicant data");
+                restoredSupplicantData = null;
+            }
+        }
+
+        void incorporateWifiConfigFile(BackupDataInput data) {
+            restoredWifiConfigFile = new byte[data.getDataSize()];
+            if (restoredWifiConfigFile.length <= 0) return;
+            try {
+                data.readEntityData(restoredWifiConfigFile, 0, data.getDataSize());
+            } catch (IOException e) {
+                Log.w(TAG, "Unable to read config file");
+                restoredWifiConfigFile = null;
+            }
+        }
+
+        @Override
+        public void run() {
+            if (restoredSupplicantData != null || restoredWifiConfigFile != null) {
+                if (DEBUG_BACKUP) {
+                    Log.v(TAG, "Starting deferred restore of wifi data");
+                }
+                final int retainedWifiState = enableWifi(false);
+                if (restoredSupplicantData != null) {
+                    restoreWifiSupplicant(FILE_WIFI_SUPPLICANT,
+                            restoredSupplicantData, restoredSupplicantData.length);
+                    FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
+                            FileUtils.S_IRUSR | FileUtils.S_IWUSR |
+                            FileUtils.S_IRGRP | FileUtils.S_IWGRP,
+                            Process.myUid(), Process.WIFI_UID);
+                }
+                if (restoredWifiConfigFile != null) {
+                    restoreFileData(mWifiConfigFile,
+                            restoredWifiConfigFile, restoredWifiConfigFile.length);
+                }
+                // restore the previous WIFI state.
+                enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED ||
+                        retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
+            }
+        }
+    }
+
+    // Instantiate the wifi-config restore runnable, scheduling it for execution
+    // a minute hence
+    void initWifiRestoreIfNecessary() {
+        if (mWifiRestore == null) {
+            mWifiRestore = new WifiRestoreRunnable();
+        }
+    }
+
     @Override
     public void onRestore(BackupDataInput data, int appVersionCode,
             ParcelFileDescriptor newState) throws IOException {
@@ -315,26 +376,26 @@
                 restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal);
             } else if (KEY_GLOBAL.equals(key)) {
                 restoreSettings(data, Settings.Global.CONTENT_URI, null);
-            } else if (NAIVE_WIFI_RESTORE && KEY_WIFI_SUPPLICANT.equals(key)) {
-                int retainedWifiState = enableWifi(false);
-                restoreWifiSupplicant(FILE_WIFI_SUPPLICANT, data);
-                FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
-                        FileUtils.S_IRUSR | FileUtils.S_IWUSR |
-                        FileUtils.S_IRGRP | FileUtils.S_IWGRP,
-                        Process.myUid(), Process.WIFI_UID);
-                // retain the previous WIFI state.
-                enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED ||
-                        retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
+            } else if (KEY_WIFI_SUPPLICANT.equals(key)) {
+                initWifiRestoreIfNecessary();
+                mWifiRestore.incorporateWifiSupplicant(data);
             } else if (KEY_LOCALE.equals(key)) {
                 byte[] localeData = new byte[size];
                 data.readEntityData(localeData, 0, size);
                 mSettingsHelper.setLocaleData(localeData, size);
-            } else if (NAIVE_WIFI_RESTORE && KEY_WIFI_CONFIG.equals(key)) {
-                restoreFileData(mWifiConfigFile, data);
+            } else if (KEY_WIFI_CONFIG.equals(key)) {
+                initWifiRestoreIfNecessary();
+                mWifiRestore.incorporateWifiConfigFile(data);
              } else {
                 data.skipEntityData();
             }
         }
+
+        // If we have wifi data to restore, post a runnable to perform the
+        // bounce-and-update operation a little ways in the future.
+        if (mWifiRestore != null) {
+            new Handler(getMainLooper()).postDelayed(mWifiRestore, WIFI_BOUNCE_DELAY_MILLIS);
+        }
     }
 
     @Override
@@ -619,7 +680,7 @@
                 getContentResolver().insert(destination, contentValues);
             }
 
-            if (DEBUG || true) {
+            if (DEBUG) {
                 Log.d(TAG, "Restored setting: " + destination + " : "+ key + "=" + value);
             }
         }
@@ -731,17 +792,6 @@
 
     }
 
-    private void restoreFileData(String filename, BackupDataInput data) {
-        byte[] bytes = new byte[data.getDataSize()];
-        if (bytes.length <= 0) return;
-        try {
-            data.readEntityData(bytes, 0, data.getDataSize());
-            restoreFileData(filename, bytes, bytes.length);
-        } catch (IOException e) {
-            Log.w(TAG, "Unable to read file data for " + filename);
-        }
-    }
-
     private void restoreFileData(String filename, byte[] bytes, int size) {
         try {
             File file = new File(filename);
@@ -794,17 +844,6 @@
         }
     }
 
-    private void restoreWifiSupplicant(String filename, BackupDataInput data) {
-        byte[] bytes = new byte[data.getDataSize()];
-        if (bytes.length <= 0) return;
-        try {
-            data.readEntityData(bytes, 0, data.getDataSize());
-            restoreWifiSupplicant(filename, bytes, bytes.length);
-        } catch (IOException e) {
-            Log.w(TAG, "Unable to read supplicant data");
-        }
-    }
-
     private void restoreWifiSupplicant(String filename, byte[] bytes, int size) {
         try {
             WifiNetworkSettings supplicantImage = new WifiNetworkSettings();
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 64b660f..f0e5a87 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -108,6 +108,7 @@
         </activity>
 
         <activity android:name=".recent.RecentsActivity"
+                android:label="@string/accessibility_desc_recent_apps"
                 android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar"
                 android:excludeFromRecents="true"
                 android:launchMode="singleInstance"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7ac27fe..e0b0227 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -377,6 +377,8 @@
     <string name="accessibility_desc_notification_shade">Notification shade.</string>
     <!-- Content description for the quick settings panel (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_desc_quick_settings">Quick settings.</string>
+    <!-- Content description for the recent apps panel (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_desc_recent_apps">Recent apps.</string>
 
     <!-- Content description of the user tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_quick_settings_user">User <xliff:g id="user" example="John Doe">%s</xliff:g>.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
index 140cc80..79069b8 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
@@ -205,4 +205,8 @@
     boolean isForeground() {
         return mForeground;
     }
+
+    boolean isActivityShowing() {
+         return mShowing;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index ff51996..8607508 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -579,7 +579,7 @@
         } else {
             mRecentTaskDescriptions.addAll(tasks);
         }
-        if (((RecentsActivity)mContext).isForeground()) {
+        if (((RecentsActivity) mContext).isActivityShowing()) {
             refreshViews();
         }
     }
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 3351e61..7e047fd 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -447,6 +447,8 @@
     // Screenshot trigger states
     // Time to volume and power must be pressed within this interval of each other.
     private static final long SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS = 150;
+    // Increase the chord delay when taking a screenshot from the keyguard
+    private static final float KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER = 2.5f;
     private boolean mScreenshotChordEnabled;
     private boolean mVolumeDownKeyTriggered;
     private long mVolumeDownKeyTime;
@@ -669,12 +671,21 @@
                 mVolumeDownKeyConsumedByScreenshotChord = true;
                 cancelPendingPowerKeyAction();
 
-                mHandler.postDelayed(mScreenshotChordLongPress,
-                        ViewConfiguration.getGlobalActionKeyTimeout());
+                mHandler.postDelayed(mScreenshotChordLongPress, getScreenshotChordLongPressDelay());
             }
         }
     }
 
+    private long getScreenshotChordLongPressDelay() {
+        if (mKeyguardMediator.isShowing()) {
+            // Double the time it takes to take a screenshot from the keyguard
+            return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER *
+                    ViewConfiguration.getGlobalActionKeyTimeout());
+        } else {
+            return ViewConfiguration.getGlobalActionKeyTimeout();
+        }
+    }
+
     private void cancelPendingScreenshotChordAction() {
         mHandler.removeCallbacks(mScreenshotChordLongPress);
     }
@@ -4291,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 21ccfed..bf7be89 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -33,6 +33,8 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.os.Looper;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.UserManager;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -43,12 +45,14 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.view.View.BaseSavedState;
 import android.view.animation.AnimationUtils;
 import android.widget.RemoteViews.OnClickHandler;
 import android.widget.ViewFlipper;
 
 import com.android.internal.R;
 import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.internal.policy.impl.keyguard.KeyguardTransportControlView.SavedState;
 import com.android.internal.widget.LockPatternUtils;
 
 import java.io.File;
@@ -64,6 +68,10 @@
     static final int APPWIDGET_HOST_ID = 0x4B455947;
     private static final String KEYGUARD_WIDGET_PREFS = "keyguard_widget_prefs";
 
+    private static final int TRANSPORT_GONE = 0;
+    private static final int TRANSPORT_INVISIBLE = 1;
+    private static final int TRANSPORT_VISIBLE = 2;
+
     private AppWidgetHost mAppWidgetHost;
     private KeyguardWidgetRegion mAppWidgetRegion;
     private KeyguardWidgetPager mAppWidgetContainer;
@@ -83,10 +91,12 @@
     private KeyguardSecurityModel mSecurityModel;
 
     private Rect mTempRect = new Rect();
+    private int mTransportState = TRANSPORT_GONE;
 
     /*package*/ interface TransportCallback {
-        void hide();
-        void show();
+        void onListenerDetached();
+        void onListenerAttached();
+        void onPlayStateChanged();
     }
 
     /*package*/ interface UserSwitcherCallback {
@@ -185,7 +195,7 @@
         mAppWidgetHost.startListening();
         maybePopulateWidgets();
         disableStatusViewInteraction();
-        showAppropriateWidgetPage();
+        post(mSwitchPageRunnable);
     }
 
     private void disableStatusViewInteraction() {
@@ -715,7 +725,7 @@
     private void addDefaultWidgets() {
         LayoutInflater inflater = LayoutInflater.from(mContext);
         inflater.inflate(R.layout.keyguard_status_view, mAppWidgetContainer, true);
-        inflater.inflate(R.layout.keyguard_transport_control_view, mAppWidgetContainer, true);
+        inflater.inflate(R.layout.keyguard_transport_control_view, this, true);
 
         inflateAndAddUserSelectorWidgetIfNecessary();
         initializeTransportControl();
@@ -724,16 +734,16 @@
     private void initializeTransportControl() {
         mTransportControl =
             (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control);
+        mTransportControl.setVisibility(View.GONE);
 
         // This code manages showing/hiding the transport control. We keep it around and only
         // add it to the hierarchy if it needs to be present.
         if (mTransportControl != null) {
             mTransportControl.setKeyguardCallback(new TransportCallback() {
-                boolean mSticky = false;
                 @Override
-                public void hide() {
+                public void onListenerDetached() {
                     int page = getWidgetPosition(R.id.keyguard_transport_control);
-                    if (page != -1 && !mSticky) {
+                    if (page != -1) {
                         if (page == mAppWidgetContainer.getCurrentPage()) {
                             // Switch back to clock view if music was showing.
                             mAppWidgetContainer
@@ -744,20 +754,23 @@
                         // from AudioManager
                         KeyguardHostView.this.addView(mTransportControl);
                         mTransportControl.setVisibility(View.GONE);
+                        mTransportState = TRANSPORT_GONE;
                     }
                 }
 
                 @Override
-                public void show() {
+                public void onListenerAttached() {
                     if (getWidgetPosition(R.id.keyguard_transport_control) == -1) {
                         KeyguardHostView.this.removeView(mTransportControl);
-                        mAppWidgetContainer.addView(mTransportControl,
-                                getWidgetPosition(R.id.keyguard_status_view) + 1);
+                        mAppWidgetContainer.addView(mTransportControl, 0);
                         mTransportControl.setVisibility(View.VISIBLE);
-                        // Once shown, leave it showing
-                        mSticky = true;
                     }
                 }
+
+                @Override
+                public void onPlayStateChanged() {
+                    mTransportControl.post(mSwitchPageRunnable);
+                }
             });
         }
     }
@@ -799,12 +812,87 @@
         }
     }
 
-    private void showAppropriateWidgetPage() {
-        int page = mAppWidgetContainer.indexOfChild(findViewById(R.id.keyguard_status_view));
-        if (mAppWidgetContainer.indexOfChild(mTransportControl) != -1) {
-            page = mAppWidgetContainer.indexOfChild(mTransportControl);
+    Runnable mSwitchPageRunnable = new Runnable() {
+        @Override
+        public void run() {
+           showAppropriateWidgetPage();
         }
-        mAppWidgetContainer.setCurrentPage(page);
+    };
+
+    static class SavedState extends BaseSavedState {
+        int transportState;
+
+        SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        private SavedState(Parcel in) {
+            super(in);
+            this.transportState = in.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            super.writeToParcel(out, flags);
+            out.writeInt(this.transportState);
+        }
+
+        public static final Parcelable.Creator<SavedState> CREATOR
+                = new Parcelable.Creator<SavedState>() {
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+
+    @Override
+    public Parcelable onSaveInstanceState() {
+        Parcelable superState = super.onSaveInstanceState();
+        SavedState ss = new SavedState(superState);
+        ss.transportState = mTransportState;
+        return ss;
+    }
+
+    @Override
+    public void onRestoreInstanceState(Parcelable state) {
+        if (!(state instanceof SavedState)) {
+            super.onRestoreInstanceState(state);
+            return;
+        }
+        SavedState ss = (SavedState) state;
+        super.onRestoreInstanceState(ss.getSuperState());
+        mTransportState = ss.transportState;
+        post(mSwitchPageRunnable);
+    }
+
+    private void showAppropriateWidgetPage() {
+
+        // The following sets the priority for showing widgets. Transport should be shown if
+        // music is playing, followed by the multi-user widget if enabled, followed by the
+        // status widget.
+        final int pageToShow;
+        if (mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE) {
+            mTransportState = TRANSPORT_VISIBLE;
+            pageToShow = mAppWidgetContainer.indexOfChild(mTransportControl);
+        } else {
+            UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+            final View multiUserView = findViewById(R.id.keyguard_multi_user_selector);
+            final int multiUserPosition = mAppWidgetContainer.indexOfChild(multiUserView);
+            if (multiUserPosition != -1 && mUm.getUsers(true).size() > 1) {
+                pageToShow = multiUserPosition;
+            } else {
+                final View statusView = findViewById(R.id.keyguard_status_view);
+                pageToShow = mAppWidgetContainer.indexOfChild(statusView);
+            }
+            if (mTransportState == TRANSPORT_VISIBLE) {
+                mTransportState = TRANSPORT_INVISIBLE;
+            }
+        }
+        mAppWidgetContainer.setCurrentPage(pageToShow);
     }
 
     private void inflateAndAddUserSelectorWidgetIfNecessary() {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
index e3b7b01..b6b731e 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
@@ -28,6 +28,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.MediaStore;
+import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Slog;
@@ -203,11 +204,18 @@
         } else if (disabledBySimState) {
             Log.v(TAG, "Camera disabled by Sim State");
         }
+        boolean currentUserSetup = 0 != Settings.Secure.getIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.USER_SETUP_COMPLETE,
+                0 /*default */,
+                currentUserHandle);
         boolean searchActionAvailable =
                 ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
                 .getAssistIntent(mContext, UserHandle.USER_CURRENT) != null;
-        mCameraDisabled = cameraDisabledByAdmin || disabledBySimState || !cameraTargetPresent;
-        mSearchDisabled = disabledBySimState || !searchActionAvailable || !searchTargetPresent;
+        mCameraDisabled = cameraDisabledByAdmin || disabledBySimState || !cameraTargetPresent
+                || !currentUserSetup;
+        mSearchDisabled = disabledBySimState || !searchActionAvailable || !searchTargetPresent
+                || !currentUserSetup;
         updateResources();
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
index e2f3059..7e71f94 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
@@ -42,7 +42,6 @@
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.View.OnClickListener;
-import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -59,7 +58,7 @@
     private static final int MSG_SET_GENERATION_ID = 104;
     private static final int MAXDIM = 512;
     private static final int DISPLAY_TIMEOUT_MS = 5000; // 5s
-    protected static final boolean DEBUG = false;
+    protected static final boolean DEBUG = true;
     protected static final String TAG = "TransportControlView";
 
     private ImageView mAlbumArt;
@@ -75,6 +74,7 @@
     private int mCurrentPlayState;
     private AudioManager mAudioManager;
     private IRemoteControlDisplayWeak mIRCD;
+    private boolean mMusicClientPresent = true;
 
     /**
      * The metadata which should be populated into the view once we've been attached
@@ -112,7 +112,9 @@
             case MSG_SET_GENERATION_ID:
                 if (msg.arg2 != 0) {
                     // This means nobody is currently registered. Hide the view.
-                    hide();
+                    onListenerDetached();
+                } else {
+                    onListenerAttached();
                 }
                 if (DEBUG) Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + msg.arg2);
                 mClientGeneration = msg.arg1;
@@ -193,28 +195,26 @@
         mIRCD = new IRemoteControlDisplayWeak(mHandler);
     }
 
-    protected void hide() {
-        if (DEBUG) Log.v(TAG, "Transport was told to hide");
+    protected void onListenerDetached() {
+        mMusicClientPresent = false;
+        if (DEBUG) Log.v(TAG, "onListenerDetached()");
         if (mTransportCallback != null) {
-            mTransportCallback.hide();
+            mTransportCallback.onListenerDetached();
         } else {
-            Log.w(TAG, "Hide music, but callback wasn't set");
+            Log.w(TAG, "onListenerDetached: no callback");
         }
     }
 
-    private void show() {
-        if (DEBUG) Log.v(TAG, "Transport was told to show");
+    private void onListenerAttached() {
+        mMusicClientPresent = true;
+        if (DEBUG) Log.v(TAG, "onListenerAttached()");
         if (mTransportCallback != null) {
-            mTransportCallback.show();
+            mTransportCallback.onListenerAttached();
         } else {
-            Log.w(TAG, "Show music, but callback wasn't set");
+            Log.w(TAG, "onListenerAttached(): no callback");
         }
     }
 
-    private void userActivity() {
-        // TODO Auto-generated method stub
-    }
-
     private void updateTransportControls(int transportControlFlags) {
         mTransportControlFlags = transportControlFlags;
     }
@@ -341,6 +341,11 @@
         updatePlayPauseState(mCurrentPlayState);
     }
 
+    public boolean isMusicPlaying() {
+       return mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING
+               || mCurrentPlayState == RemoteControlClient.PLAYSTATE_BUFFERING;
+    }
+
     private static void setVisibilityBasedOnFlag(View view, int flags, int flag) {
         if ((flags & flag) != 0) {
             view.setVisibility(View.VISIBLE);
@@ -357,7 +362,6 @@
         }
         final int imageResId;
         final int imageDescId;
-        boolean showIfHidden = false;
         switch (state) {
             case RemoteControlClient.PLAYSTATE_ERROR:
                 imageResId = com.android.internal.R.drawable.stat_sys_warning;
@@ -369,32 +373,27 @@
             case RemoteControlClient.PLAYSTATE_PLAYING:
                 imageResId = com.android.internal.R.drawable.ic_media_pause;
                 imageDescId = com.android.internal.R.string.lockscreen_transport_pause_description;
-                showIfHidden = true;
                 break;
 
             case RemoteControlClient.PLAYSTATE_BUFFERING:
                 imageResId = com.android.internal.R.drawable.ic_media_stop;
                 imageDescId = com.android.internal.R.string.lockscreen_transport_stop_description;
-                showIfHidden = true;
                 break;
 
             case RemoteControlClient.PLAYSTATE_PAUSED:
             default:
                 imageResId = com.android.internal.R.drawable.ic_media_play;
                 imageDescId = com.android.internal.R.string.lockscreen_transport_play_description;
-                showIfHidden = false;
                 break;
         }
         mBtnPlay.setImageResource(imageResId);
         mBtnPlay.setContentDescription(getResources().getString(imageDescId));
-        if (showIfHidden) {
-            show();
-        }
         mCurrentPlayState = state;
+        mTransportCallback.onPlayStateChanged();
     }
 
     static class SavedState extends BaseSavedState {
-        boolean wasShowing;
+        boolean clientPresent;
 
         SavedState(Parcelable superState) {
             super(superState);
@@ -402,13 +401,13 @@
 
         private SavedState(Parcel in) {
             super(in);
-            this.wasShowing = in.readInt() != 0;
+            this.clientPresent = in.readInt() != 0;
         }
 
         @Override
         public void writeToParcel(Parcel out, int flags) {
             super.writeToParcel(out, flags);
-            out.writeInt(this.wasShowing ? 1 : 0);
+            out.writeInt(this.clientPresent ? 1 : 0);
         }
 
         public static final Parcelable.Creator<SavedState> CREATOR
@@ -425,24 +424,23 @@
 
     @Override
     public Parcelable onSaveInstanceState() {
-        if (DEBUG) Log.v(TAG, "onSaveInstanceState()");
         Parcelable superState = super.onSaveInstanceState();
         SavedState ss = new SavedState(superState);
-        ss.wasShowing = getVisibility() == View.VISIBLE;
+        ss.clientPresent = mMusicClientPresent;
         return ss;
     }
 
     @Override
     public void onRestoreInstanceState(Parcelable state) {
-        if (DEBUG) Log.v(TAG, "onRestoreInstanceState()");
         if (!(state instanceof SavedState)) {
             super.onRestoreInstanceState(state);
             return;
         }
         SavedState ss = (SavedState) state;
         super.onRestoreInstanceState(ss.getSuperState());
-        if (ss.wasShowing) {
-            show();
+        if (ss.clientPresent) {
+            if (DEBUG) Log.v(TAG, "Reattaching client because it was attached");
+            onListenerAttached();
         }
     }
 
@@ -458,7 +456,6 @@
         }
         if (keyCode != -1) {
             sendMediaButtonClick(keyCode);
-            userActivity();
         }
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
index 33ff71e..d25444f 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
@@ -48,7 +48,7 @@
  * reported to this class by the current {@link KeyguardViewBase}.
  */
 public class KeyguardViewManager {
-    private final static boolean DEBUG = false;
+    private final static boolean DEBUG = true;
     private static String TAG = "KeyguardViewManager";
     public static boolean USE_UPPER_CASE = true;
 
@@ -359,6 +359,12 @@
 
         if (mKeyguardHost != null) {
             mKeyguardHost.setVisibility(View.GONE);
+
+            // We really only want to preserve keyguard state for configuration changes. Hence
+            // we should clear state of widgets (e.g. Music) when we hide keyguard so it can
+            // start with a fresh state when we return.
+            mStateContainer.clear();
+
             // Don't do this right away, so we can let the view continue to animate
             // as it goes away.
             if (mKeyguardView != null) {
diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java
index e7cd279..18182cd 100755
--- a/services/java/com/android/server/BluetoothManagerService.java
+++ b/services/java/com/android/server/BluetoothManagerService.java
@@ -4,6 +4,7 @@
 
 package com.android.server;
 
+import android.app.ActivityManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.IBluetooth;
 import android.bluetooth.IBluetoothCallback;
@@ -17,17 +18,21 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.os.Binder;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Message;
+import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
-import android.os.Binder;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
-import java.util.List;
 import java.util.ArrayList;
+import java.util.List;
 class BluetoothManagerService extends IBluetoothManager.Stub {
     private static final String TAG = "BluetoothManagerService";
     private static final boolean DBG = true;
@@ -42,6 +47,8 @@
     private static final int TIMEOUT_SAVE_MS = 500; //Maximum msec to wait for a save
     //Maximum msec to wait for service restart
     private static final int SERVICE_RESTART_TIME_MS = 200;
+    //Maximum msec to delay MESSAGE_USER_SWITCHED
+    private static final int USER_SWITCHED_TIME_MS = 200;
 
     private static final int MESSAGE_ENABLE = 1;
     private static final int MESSAGE_DISABLE = 2;
@@ -57,6 +64,7 @@
     private static final int MESSAGE_TIMEOUT_UNBIND =101;
     private static final int MESSAGE_GET_NAME_AND_ADDRESS=200;
     private static final int MESSAGE_SAVE_NAME_AND_ADDRESS=201;
+    private static final int MESSAGE_USER_SWITCHED = 300;
     private static final int MAX_SAVE_RETRIES=3;
 
     private final Context mContext;
@@ -72,6 +80,10 @@
     private boolean mBinding;
     private boolean mUnbinding;
     private boolean mQuietEnable = false;
+    private boolean mEnable;
+    private int mState;
+    private HandlerThread mThread;
+    private final BluetoothHandler mHandler;
 
     private void registerForAirplaneMode(IntentFilter filter) {
         final ContentResolver resolver = mContext.getContentResolver();
@@ -106,23 +118,32 @@
                 }
             } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
                 if (isAirplaneModeOn()) {
-                        // disable without persisting the setting
-                        handleDisable(false);
-                } else {
-                    if (isBluetoothPersistedStateOn()) {
-                        // enable without persisting the setting
-                        handleEnable(false, false);
-                    }
+                    // disable without persisting the setting
+                    mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE,
+                           0, 0));
+                } else if (isBluetoothPersistedStateOn()) {
+                    // enable without persisting the setting
+                    mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE,
+                           0, 0));
                 }
+            } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+                mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_SWITCHED,
+                       intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
             }
         }
     };
 
     BluetoothManagerService(Context context) {
+        mThread = new HandlerThread("BluetoothManager");
+        mThread.start();
+        mHandler = new BluetoothHandler(mThread.getLooper());
+
         mContext = context;
         mBluetooth = null;
         mBinding = false;
         mUnbinding = false;
+        mEnable = false;
+        mState = BluetoothAdapter.STATE_OFF;
         mAddress = null;
         mName = null;
         mContentResolver = context.getContentResolver();
@@ -130,6 +151,7 @@
         mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>();
         IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
         filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
+        filter.addAction(Intent.ACTION_USER_SWITCHED);
         registerForAirplaneMode(filter);
         mContext.registerReceiver(mReceiver, filter);
         boolean airplaneModeOn = isAirplaneModeOn();
@@ -139,7 +161,7 @@
         if (bluetoothOn) {
             //Enable
             if (DBG) Log.d(TAG, "Auto-enabling Bluetooth.");
-            enable();
+            enableHelper();
         } else if (!isNameAndAddressSet()) {
             //Sync the Bluetooth name and address from the Bluetooth Adapter
             if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address...");
@@ -251,6 +273,11 @@
     }
 
     public boolean isEnabled() {
+        if (!checkIfCallerIsForegroundUser()) {
+            Log.w(TAG,"isEnabled(): not allowed for non-active user");
+            return false;
+        }
+
         synchronized(mConnection) {
             try {
                 return (mBluetooth != null && mBluetooth.isEnabled());
@@ -266,10 +293,6 @@
             Log.d(TAG,"getNameAndAddress(): mBluetooth = " + mBluetooth +
                   " mBinding = " + mBinding);
         }
-        synchronized(mConnection) {
-            if (mBinding) return;
-            if (mConnection == null) mBinding = true;
-        }
         Message msg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
         mHandler.sendMessage(msg);
     }
@@ -277,21 +300,19 @@
     {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH ADMIN permission");
+
+        if (!checkIfCallerIsForegroundUser()) {
+            Log.w(TAG,"enableNoAutoConnect(): not allowed for non-active user");
+            return false;
+        }
+
         if (DBG) {
             Log.d(TAG,"enableNoAutoConnect():  mBluetooth =" + mBluetooth +
                     " mBinding = " + mBinding);
         }
-        if (Binder.getCallingUid() != android.os.Process.NFC_UID) {
+        if (Binder.getCallingUid() != Process.NFC_UID) {
             throw new SecurityException("no permission to enable Bluetooth quietly");
         }
-        synchronized(mConnection) {
-            if (mBinding) {
-                Log.w(TAG,"enableNoAutoConnect(): binding in progress. Returning..");
-                return true;
-            }
-            if (mConnection == null) mBinding = true;
-        }
-
         Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
         msg.arg1=0; //No persist
         msg.arg2=1; //Quiet mode
@@ -300,39 +321,28 @@
 
     }
     public boolean enable() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH ADMIN permission");
-        if (DBG) {
-            Log.d(TAG,"enable():  mBluetooth =" + mBluetooth +
-                    " mBinding = " + mBinding);
+        if (!checkIfCallerIsForegroundUser()) {
+            Log.w(TAG,"enable(): not allowed for non-active user");
+            return false;
         }
 
-        synchronized(mConnection) {
-            if (mBinding) {
-                Log.w(TAG,"enable(): binding in progress. Returning..");
-                return true;
-            }
-            if (mConnection == null) mBinding = true;
-        }
-
-        Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
-        msg.arg1=1; //persist
-        msg.arg2=0; //No Quiet Mode
-        mHandler.sendMessage(msg);
-        return true;
+        return enableHelper();
     }
 
     public boolean disable(boolean persist) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH ADMIN permissicacheNameAndAddresson");
+
+        if (!checkIfCallerIsForegroundUser()) {
+            Log.w(TAG,"disable(): not allowed for non-active user");
+            return false;
+        }
+
         if (DBG) {
             Log.d(TAG,"disable(): mBluetooth = " + mBluetooth +
                 " mBinding = " + mBinding);
         }
 
-        synchronized(mConnection) {
-             if (mBluetooth == null) return false;
-        }
         Message msg = mHandler.obtainMessage(MESSAGE_DISABLE);
         msg.arg1=(persist?1:0);
         mHandler.sendMessage(msg);
@@ -348,13 +358,13 @@
         synchronized (mConnection) {
             if (mUnbinding) return;
             mUnbinding = true;
-            if (mConnection != null) {
+            if (mBluetooth != null) {
                 if (!mConnection.isGetNameAddressOnly()) {
                     //Unregister callback object
                     try {
                         mBluetooth.unregisterCallback(mBluetoothCallback);
                     } catch (RemoteException re) {
-                        Log.e(TAG, "Unable to register BluetoothCallback",re);
+                        Log.e(TAG, "Unable to unregister BluetoothCallback",re);
                     }
                 }
                 if (DBG) Log.d(TAG, "Sending unbind request.");
@@ -362,6 +372,7 @@
                 //Unbind
                 mContext.unbindService(mConnection);
                 mUnbinding = false;
+                mBinding = false;
             } else {
                 mUnbinding=false;
             }
@@ -382,6 +393,24 @@
     }
 
     /**
+     * Inform BluetoothAdapter instances that Adapter service is up
+     */
+    private void sendBluetoothServiceUpCallback() {
+        if (!mConnection.isGetNameAddressOnly()) {
+            if (DBG) Log.d(TAG,"Calling onBluetoothServiceUp callbacks");
+            int n = mCallbacks.beginBroadcast();
+            Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
+            for (int i=0; i <n;i++) {
+                try {
+                    mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
+                }  catch (RemoteException e) {
+                    Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
+                }
+            }
+            mCallbacks.finishBroadcast();
+        }
+    }
+    /**
      * Inform BluetoothAdapter instances that Adapter service is down
      */
     private void sendBluetoothServiceDownCallback() {
@@ -402,6 +431,12 @@
     public String getAddress() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH ADMIN permission");
+
+        if (!checkIfCallerIsForegroundUser()) {
+            Log.w(TAG,"getAddress(): not allowed for non-active user");
+            return mAddress;
+        }
+
         synchronized(mConnection) {
             if (mBluetooth != null) {
                 try {
@@ -420,6 +455,12 @@
     public String getName() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH ADMIN permission");
+
+        if (!checkIfCallerIsForegroundUser()) {
+            Log.w(TAG,"getName(): not allowed for non-active user");
+            return mName;
+        }
+
         synchronized(mConnection) {
             if (mBluetooth != null) {
                 try {
@@ -464,7 +505,11 @@
 
     private BluetoothServiceConnection mConnection = new BluetoothServiceConnection();
 
-    private final Handler mHandler = new Handler() {
+    private class BluetoothHandler extends Handler {
+        public BluetoothHandler(Looper looper) {
+            super(looper);
+        }
+
         @Override
         public void handleMessage(Message msg) {
             if (DBG) Log.d (TAG, "Message: " + msg.what);
@@ -473,7 +518,7 @@
                     if (DBG) Log.d(TAG,"MESSAGE_GET_NAME_AND_ADDRESS");
                     synchronized(mConnection) {
                         //Start bind request
-                        if (mBluetooth == null) {
+                        if ((mBluetooth == null) && (!mBinding)) {
                             if (DBG) Log.d(TAG, "Binding to service to get name and address");
                             mConnection.setGetNameAddressOnly(true);
                             //Start bind timeout and bind
@@ -484,11 +529,20 @@
                                   Context.BIND_AUTO_CREATE, UserHandle.USER_CURRENT)) {
                                 mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
                                 Log.e(TAG, "fail to bind to: " + IBluetooth.class.getName());
+                            } else {
+                                mBinding = true;
                             }
                         }
                         else {
                             Message saveMsg= mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
-                            mHandler.sendMessage(saveMsg);
+                            saveMsg.arg1 = 0;
+                            if (mBluetooth != null) {
+                                mHandler.sendMessage(saveMsg);
+                            } else {
+                                // if enable is also called to bind the service
+                                // wait for MESSAGE_BLUETOOTH_SERVICE_CONNECTED
+                                mHandler.sendMessageDelayed(saveMsg, TIMEOUT_SAVE_MS);
+                            }
                         }
                     }
                     break;
@@ -508,8 +562,9 @@
 
                             if (name != null && address != null) {
                                 storeNameAndAddress(name,address);
-                                sendBluetoothServiceDownCallback();
-                                unbindAndFinish();
+                                if (mConnection.isGetNameAddressOnly()) {
+                                    unbindAndFinish();
+                                }
                             } else {
                                 if (msg.arg1 < MAX_SAVE_RETRIES) {
                                     Message retryMsg = mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
@@ -518,10 +573,17 @@
                                     mHandler.sendMessageDelayed(retryMsg, TIMEOUT_SAVE_MS);
                                 } else {
                                     Log.w(TAG,"Maximum name/address remote retrieval retry exceeded");
-                                    sendBluetoothServiceDownCallback();
-                                    unbindAndFinish();
+                                    if (mConnection.isGetNameAddressOnly()) {
+                                        unbindAndFinish();
+                                    }
                                 }
                             }
+                        } else {
+                            // rebind service by Request GET NAME AND ADDRESS
+                            // if service is unbinded by disable or
+                            // MESSAGE_BLUETOOTH_SERVICE_CONNECTED is not received
+                            Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
+                            mHandler.sendMessage(getMsg);
                         }
                     }
                     break;
@@ -530,12 +592,22 @@
                     if (DBG) {
                         Log.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth);
                     }
-
+                    mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
+                    mEnable = true;
                     handleEnable(msg.arg1 == 1, msg.arg2 ==1);
                     break;
 
                 case MESSAGE_DISABLE:
-                    handleDisable(msg.arg1 == 1);
+                    mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
+                    if (mEnable && mBluetooth != null) {
+                        waitForOnOff(true, false);
+                        mEnable = false;
+                        handleDisable(msg.arg1 == 1);
+                        waitForOnOff(false, false);
+                    } else {
+                        mEnable = false;
+                        handleDisable(msg.arg1 == 1);
+                    }
                     break;
 
                 case MESSAGE_REGISTER_ADAPTER:
@@ -580,27 +652,26 @@
                             //Request GET NAME AND ADDRESS
                             Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
                             mHandler.sendMessage(getMsg);
-                            return;
+                            if (!mEnable) return;
                         }
 
+                        mConnection.setGetNameAddressOnly(false);
                         //Register callback object
                         try {
                             mBluetooth.registerCallback(mBluetoothCallback);
                         } catch (RemoteException re) {
                             Log.e(TAG, "Unable to register BluetoothCallback",re);
                         }
-
                         //Inform BluetoothAdapter instances that service is up
-                        int n = mCallbacks.beginBroadcast();
-                        Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
-                        for (int i=0; i <n;i++) {
+                        sendBluetoothServiceUpCallback();
+
+                        //Check if name and address is loaded if not get it first.
+                        if (!isNameAndAddressSet()) {
                             try {
-                                mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
-                            } catch (RemoteException e) {
-                                Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
-                            }
+                                storeNameAndAddress(mBluetooth.getName(),
+                                                    mBluetooth.getAddress());
+                            } catch (RemoteException e) {Log.e(TAG, "", e);};
                         }
-                        mCallbacks.finishBroadcast();
 
                         //Do enable request
                         try {
@@ -619,12 +690,19 @@
                             Log.e(TAG,"Unable to call enable()",e);
                         }
                     }
+
+                    if (!mEnable) {
+                        waitForOnOff(true, false);
+                        handleDisable(false);
+                        waitForOnOff(false, false);
+                    }
                     break;
                 }
                 case MESSAGE_TIMEOUT_BIND: {
                     Log.e(TAG, "MESSAGE_TIMEOUT_BIND");
                     synchronized(mConnection) {
                         mBinding = false;
+                        mEnable = false;
                     }
                     break;
                 }
@@ -633,51 +711,37 @@
                     int prevState = msg.arg1;
                     int newState = msg.arg2;
                     if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: prevState = " + prevState + ", newState=" + newState);
-                    if (prevState != newState) {
-                        //Notify all proxy objects first of adapter state change
-                        if (newState == BluetoothAdapter.STATE_ON || newState == BluetoothAdapter.STATE_OFF) {
-                            boolean isUp = (newState==BluetoothAdapter.STATE_ON);
-                            sendBluetoothStateCallback(isUp);
-
-                            //If Bluetooth is off, send service down event to proxy objects, and unbind
-                            if (!isUp) {
-                                sendBluetoothServiceDownCallback();
-                                unbindAndFinish();
-                            }
-                        }
-
-                        //Send broadcast message to everyone else
-                        Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
-                        intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
-                        intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
-                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-                        if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState);
-                        mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
-                                BLUETOOTH_PERM);
-                    }
+                    mState = newState;
+                    bluetoothStateChangeHandler(prevState, newState);
                     break;
                 }
                 case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
                 {
-                    if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED");
-                    sendBluetoothServiceDownCallback();
-
-                    // Send BT state broadcast to update
-                    // the BT icon correctly
-                    Message stateChangeMsg = mHandler.obtainMessage(
-                        MESSAGE_BLUETOOTH_STATE_CHANGE);
-                    stateChangeMsg.arg1 = BluetoothAdapter.STATE_ON;
-                    stateChangeMsg.arg2 =
-                        BluetoothAdapter.STATE_TURNING_OFF;
-                    mHandler.sendMessage(stateChangeMsg);
+                    Log.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED");
                     synchronized(mConnection) {
+                        // if service is unbinded already, do nothing and return
+                        if (mBluetooth == null) return;
                         mBluetooth = null;
                     }
-                    // Send a Bluetooth Restart message
-                    Message restartMsg = mHandler.obtainMessage(
-                        MESSAGE_RESTART_BLUETOOTH_SERVICE);
-                    mHandler.sendMessageDelayed(restartMsg,
-                        SERVICE_RESTART_TIME_MS);
+
+                    if (mEnable) {
+                        mEnable = false;
+                        // Send a Bluetooth Restart message
+                        Message restartMsg = mHandler.obtainMessage(
+                            MESSAGE_RESTART_BLUETOOTH_SERVICE);
+                        mHandler.sendMessageDelayed(restartMsg,
+                            SERVICE_RESTART_TIME_MS);
+                    }
+
+                    if (!mConnection.isGetNameAddressOnly()) {
+                        sendBluetoothServiceDownCallback();
+
+                        // Send BT state broadcast to update
+                        // the BT icon correctly
+                        bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
+                                                    BluetoothAdapter.STATE_TURNING_OFF);
+                        mState = BluetoothAdapter.STATE_OFF;
+                    }
                     break;
                 }
                 case MESSAGE_RESTART_BLUETOOTH_SERVICE:
@@ -687,6 +751,7 @@
                     /* Enable without persisting the setting as
                      it doesnt change when IBluetooth
                      service restarts */
+                    mEnable = true;
                     handleEnable(false, mQuietEnable);
                     break;
                 }
@@ -699,9 +764,66 @@
                     }
                     break;
                 }
+
+                case MESSAGE_USER_SWITCHED:
+                {
+                    if (DBG) {
+                        Log.d(TAG, "MESSAGE_USER_SWITCHED");
+                    }
+                    mHandler.removeMessages(MESSAGE_USER_SWITCHED);
+                    /* disable and enable BT when detect a user switch */
+                    if (mEnable && mBluetooth != null) {
+                        synchronized (mConnection) {
+                            if (mBluetooth != null) {
+                                //Unregister callback object
+                                try {
+                                    mBluetooth.unregisterCallback(mBluetoothCallback);
+                                } catch (RemoteException re) {
+                                    Log.e(TAG, "Unable to unregister",re);
+                                }
+                            }
+                        }
+                        mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
+
+                        waitForOnOff(true, false);
+
+                        bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON);
+
+                        // disable
+                        handleDisable(false);
+
+                        waitForOnOff(false, true);
+
+                        bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
+                                                    BluetoothAdapter.STATE_OFF);
+                        mState = BluetoothAdapter.STATE_OFF;
+                        sendBluetoothServiceDownCallback();
+                        synchronized (mConnection) {
+                            if (mBluetooth != null) {
+                                mBluetooth = null;
+                                //Unbind
+                                mContext.unbindService(mConnection);
+                            }
+                        }
+                        SystemClock.sleep(100);
+
+                        // enable
+                        handleEnable(false, mQuietEnable);
+		    } else if (mBinding || mBluetooth != null) {
+                        Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED);
+                        userMsg.arg2 = 1 + msg.arg2;
+                        // if user is switched when service is being binding
+                        // delay sending MESSAGE_USER_SWITCHED
+                        mHandler.sendMessageDelayed(userMsg, USER_SWITCHED_TIME_MS);
+                        if (DBG) {
+                            Log.d(TAG, "delay MESSAGE_USER_SWITCHED " + userMsg.arg2);
+                        }
+		    }
+                    break;
+                }
             }
         }
-    };
+    }
 
     private void handleEnable(boolean persist, boolean quietMode) {
         if (persist) {
@@ -711,18 +833,35 @@
         mQuietEnable = quietMode;
 
         synchronized(mConnection) {
-            if (mBluetooth == null) {
+            if ((mBluetooth == null) && (!mBinding)) {
                 //Start bind timeout and bind
                 Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
                 mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
                 mConnection.setGetNameAddressOnly(false);
                 Intent i = new Intent(IBluetooth.class.getName());
-                if (!mContext.bindService(i, mConnection, Context.BIND_AUTO_CREATE,
+                if (!mContext.bindService(i, mConnection,Context.BIND_AUTO_CREATE,
                                           UserHandle.USER_CURRENT)) {
                     mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
                     Log.e(TAG, "Fail to bind to: " + IBluetooth.class.getName());
+                } else {
+                    mBinding = true;
                 }
-            } else {
+            } else if (mBluetooth != null) {
+                if (mConnection.isGetNameAddressOnly()) {
+                    // if GetNameAddressOnly is set, we can clear this flag,
+                    // so the service won't be unbind
+                    // after name and address are saved
+                    mConnection.setGetNameAddressOnly(false);
+                    //Register callback object
+                    try {
+                        mBluetooth.registerCallback(mBluetoothCallback);
+                    } catch (RemoteException re) {
+                        Log.e(TAG, "Unable to register BluetoothCallback",re);
+                    }
+                    //Inform BluetoothAdapter instances that service is up
+                    sendBluetoothServiceUpCallback();
+                }
+
                 //Check if name and address is loaded if not get it first.
                 if (!isNameAndAddressSet()) {
                     try {
@@ -751,12 +890,14 @@
     }
 
     private void handleDisable(boolean persist) {
+        if (persist) {
+            persistBluetoothSetting(false);
+        }
+
         synchronized(mConnection) {
-            if (mBluetooth != null ) {
-                if (persist) {
-                    persistBluetoothSetting(false);
-                }
-                mConnection.setGetNameAddressOnly(false);
+            // don't need to disable if GetNameAddressOnly is set,
+            // service will be unbinded after Name and Address are saved
+            if ((mBluetooth != null) && (!mConnection.isGetNameAddressOnly())) {
                 if (DBG) Log.d(TAG,"Sending off request.");
 
                 try {
@@ -769,4 +910,102 @@
             }
         }
     }
+
+    private boolean checkIfCallerIsForegroundUser() {
+        int foregroundUser;
+        int callingUser = UserHandle.getCallingUserId();
+        long callingIdentity = Binder.clearCallingIdentity();
+        boolean valid = false;
+        try {
+            foregroundUser = ActivityManager.getCurrentUser();
+            valid = (callingUser == foregroundUser);
+            if (DBG) {
+                Log.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid
+                    + " callingUser=" + callingUser
+                    + " foregroundUser=" + foregroundUser);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentity);
+        }
+        return valid;
+    }
+
+    private boolean enableHelper() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                "Need BLUETOOTH ADMIN permission");
+        if (DBG) {
+            Log.d(TAG,"enable():  mBluetooth =" + mBluetooth +
+                    " mBinding = " + mBinding);
+        }
+
+        Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
+        msg.arg1=1; //persist
+        msg.arg2=0; //No Quiet Mode
+        mHandler.sendMessage(msg);
+        return true;
+    }
+
+    private void bluetoothStateChangeHandler(int prevState, int newState) {
+        if (prevState != newState) {
+            //Notify all proxy objects first of adapter state change
+            if (newState == BluetoothAdapter.STATE_ON || newState == BluetoothAdapter.STATE_OFF) {
+                boolean isUp = (newState==BluetoothAdapter.STATE_ON);
+                sendBluetoothStateCallback(isUp);
+
+                //If Bluetooth is off, send service down event to proxy objects, and unbind
+                if (!isUp) {
+                    //Only unbind with mEnable flag not set
+                    //For race condition: disable and enable back-to-back
+                    //Avoid unbind right after enable due to callback from disable
+                    if ((!mEnable) && (mBluetooth != null)) {
+                        sendBluetoothServiceDownCallback();
+                        unbindAndFinish();
+                    }
+                }
+            }
+
+            //Send broadcast message to everyone else
+            Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
+            intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
+            intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+            if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                    BLUETOOTH_PERM);
+        }
+    }
+
+    /**
+     *  if on is true, wait for state become ON
+     *  if off is true, wait for state become OFF
+     *  if both on and off are false, wait for state not ON
+     */
+    private boolean waitForOnOff(boolean on, boolean off) {
+        int i = 0;
+        while (i < 10) {
+            synchronized(mConnection) {
+                try {
+                    if (mBluetooth == null) break;
+                    if (on) {
+                        if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true;
+                    } else if (off) {
+                        if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true;
+		    } else {
+                        if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true;
+		    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "getState()", e);
+                    break;
+                }
+            }
+            if (on || off) {
+                SystemClock.sleep(300);
+	    } else {
+                SystemClock.sleep(50);
+	    }
+            i++;
+        }
+        Log.e(TAG,"waitForOnOff time out");
+        return false;
+    }
 }
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index e4998e11..ffbfef6 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -900,7 +900,7 @@
             Slog.d(TAG, "--- calledFromForegroundUserOrSystemProcess ? "
                     + "calling uid = " + uid + " system uid = " + Process.SYSTEM_UID
                     + " calling userId = " + userId + ", foreground user id = "
-                    + mSettings.getCurrentUserId() + ", calling uid = " + Binder.getCallingPid());
+                    + mSettings.getCurrentUserId() + ", calling pid = " + Binder.getCallingPid());
         }
         if (uid == Process.SYSTEM_UID || userId == mSettings.getCurrentUserId()) {
             return true;
@@ -2673,6 +2673,8 @@
             mSwitchingDialog.setCanceledOnTouchOutside(true);
             mSwitchingDialog.getWindow().setType(
                     WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
+            mSwitchingDialog.getWindow().getAttributes().privateFlags |=
+                    WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
             mSwitchingDialog.getWindow().getAttributes().setTitle("Select input method");
             mSwitchingDialog.show();
         }
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index e73d599..c5016e6 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -226,7 +226,7 @@
                    updateProvidersLocked();
                }
             }
-        });
+        }, UserHandle.USER_ALL);
         mPackageMonitor.register(mContext, Looper.myLooper(), true);
 
         // listen for user change
@@ -289,7 +289,7 @@
                 mContext,
                 LocationManager.NETWORK_PROVIDER,
                 NETWORK_LOCATION_SERVICE_ACTION,
-                providerPackageNames, mLocationHandler);
+                providerPackageNames, mLocationHandler, mCurrentUserId);
         if (networkProvider != null) {
             mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
             mProxyProviders.add(networkProvider);
@@ -303,18 +303,20 @@
                 mContext,
                 LocationManager.FUSED_PROVIDER,
                 FUSED_LOCATION_SERVICE_ACTION,
-                providerPackageNames, mLocationHandler);
+                providerPackageNames, mLocationHandler, mCurrentUserId);
         if (fusedLocationProvider != null) {
             addProviderLocked(fusedLocationProvider);
             mProxyProviders.add(fusedLocationProvider);
             mEnabledProviders.add(fusedLocationProvider.getName());
+            mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedLocationProvider);
         } else {
             Slog.e(TAG, "no fused location provider found",
                     new IllegalStateException("Location service needs a fused location provider"));
         }
 
         // bind to geocoder provider
-        mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames);
+        mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames,
+                mCurrentUserId);
         if (mGeocodeProvider == null) {
             Slog.e(TAG,  "no geocoder provider found");
         }
@@ -326,11 +328,14 @@
      */
     private void switchUser(int userId) {
         mBlacklist.switchUser(userId);
-        //Log.d("LocationManagerService", "switchUser(" + mCurrentUserId + " -> " + userId + ")"); // TODO: remove this
         synchronized (mLock) {
-            // TODO: inform previous user's Receivers that they will no longer receive updates
+            mLastLocation.clear();
+            for (LocationProviderInterface p : mProviders) {
+                updateProviderListenersLocked(p.getName(), false, mCurrentUserId);
+                p.switchUser(userId);
+            }
             mCurrentUserId = userId;
-            // TODO: inform new user's Receivers that they are back on the update train
+            updateProvidersLocked();
         }
     }
 
@@ -587,7 +592,10 @@
     }
 
 
-    private boolean isAllowedBySettingsLocked(String provider) {
+    private boolean isAllowedBySettingsLocked(String provider, int userId) {
+        if (userId != mCurrentUserId) {
+            return false;
+        }
         if (mEnabledProviders.contains(provider)) {
             return true;
         }
@@ -597,7 +605,7 @@
         // Use system settings
         ContentResolver resolver = mContext.getContentResolver();
 
-        return Settings.Secure.isLocationProviderEnabled(resolver, provider);
+        return Settings.Secure.isLocationProviderEnabledForUser(resolver, provider, mCurrentUserId);
     }
 
     /**
@@ -695,24 +703,30 @@
     @Override
     public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
         ArrayList<String> out;
-        synchronized (mLock) {
-            out = new ArrayList<String>(mProviders.size());
-            for (LocationProviderInterface provider : mProviders) {
-                String name = provider.getName();
-                if (LocationManager.FUSED_PROVIDER.equals(name)) {
-                    continue;
-                }
-                if (isAllowedProviderSafe(name)) {
-                    if (enabledOnly && !isAllowedBySettingsLocked(name)) {
+        int callingUserId = UserHandle.getCallingUserId();
+        long identity = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                out = new ArrayList<String>(mProviders.size());
+                for (LocationProviderInterface provider : mProviders) {
+                    String name = provider.getName();
+                    if (LocationManager.FUSED_PROVIDER.equals(name)) {
                         continue;
                     }
-                    if (criteria != null && !LocationProvider.propertiesMeetCriteria(
-                            name, provider.getProperties(), criteria)) {
-                        continue;
+                    if (isAllowedProviderSafe(name)) {
+                        if (enabledOnly && !isAllowedBySettingsLocked(name, callingUserId)) {
+                            continue;
+                        }
+                        if (criteria != null && !LocationProvider.propertiesMeetCriteria(
+                                name, provider.getProperties(), criteria)) {
+                            continue;
+                        }
+                        out.add(name);
                     }
-                    out.add(name);
                 }
             }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
 
         if (D) Log.d(TAG, "getProviders()=" + out);
@@ -778,12 +792,12 @@
             LocationProviderInterface p = mProviders.get(i);
             boolean isEnabled = p.isEnabled();
             String name = p.getName();
-            boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
+            boolean shouldBeEnabled = isAllowedBySettingsLocked(name, mCurrentUserId);
             if (isEnabled && !shouldBeEnabled) {
-                updateProviderListenersLocked(name, false);
+                updateProviderListenersLocked(name, false, mCurrentUserId);
                 changesMade = true;
             } else if (!isEnabled && shouldBeEnabled) {
-                updateProviderListenersLocked(name, true);
+                updateProviderListenersLocked(name, true, mCurrentUserId);
                 changesMade = true;
             }
         }
@@ -793,7 +807,7 @@
         }
     }
 
-    private void updateProviderListenersLocked(String provider, boolean enabled) {
+    private void updateProviderListenersLocked(String provider, boolean enabled, int userId) {
         int listeners = 0;
 
         LocationProviderInterface p = mProvidersByName.get(provider);
@@ -806,14 +820,16 @@
             final int N = records.size();
             for (int i = 0; i < N; i++) {
                 UpdateRecord record = records.get(i);
-                // Sends a notification message to the receiver
-                if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
-                    if (deadReceivers == null) {
-                        deadReceivers = new ArrayList<Receiver>();
+                if (UserHandle.getUserId(record.mReceiver.mUid) == userId) {
+                    // Sends a notification message to the receiver
+                    if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
+                        if (deadReceivers == null) {
+                            deadReceivers = new ArrayList<Receiver>();
+                        }
+                        deadReceivers.add(record.mReceiver);
                     }
-                    deadReceivers.add(record.mReceiver);
+                    listeners++;
                 }
-                listeners++;
             }
         }
 
@@ -843,12 +859,13 @@
 
         if (records != null) {
             for (UpdateRecord record : records) {
-                LocationRequest locationRequest = record.mRequest;
-
-                providerRequest.locationRequests.add(locationRequest);
-                if (locationRequest.getInterval() < providerRequest.interval) {
-                    providerRequest.reportLocation = true;
-                    providerRequest.interval = locationRequest.getInterval();
+                if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) {
+                    LocationRequest locationRequest = record.mRequest;
+                    providerRequest.locationRequests.add(locationRequest);
+                    if (locationRequest.getInterval() < providerRequest.interval) {
+                        providerRequest.reportLocation = true;
+                        providerRequest.interval = locationRequest.getInterval();
+                    }
                 }
             }
 
@@ -860,9 +877,11 @@
                 // under that threshold.
                 long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
                 for (UpdateRecord record : records) {
-                    LocationRequest locationRequest = record.mRequest;
-                    if (locationRequest.getInterval() <= thresholdInterval) {
-                        worksource.add(record.mReceiver.mUid);
+                    if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) {
+                        LocationRequest locationRequest = record.mRequest;
+                        if (locationRequest.getInterval() <= thresholdInterval) {
+                            worksource.add(record.mReceiver.mUid);
+                        }
                     }
                 }
             }
@@ -1084,7 +1103,7 @@
             oldRecord.disposeLocked(false);
         }
 
-        boolean isProviderEnabled = isAllowedBySettingsLocked(name);
+        boolean isProviderEnabled = isAllowedBySettingsLocked(name, UserHandle.getUserId(uid));
         if (isProviderEnabled) {
             applyRequirementsLocked(name);
         } else {
@@ -1141,7 +1160,7 @@
         // update provider
         for (String provider : providers) {
             // If provider is already disabled, don't need to do anything
-            if (!isAllowedBySettingsLocked(provider)) {
+            if (!isAllowedBySettingsLocked(provider, mCurrentUserId)) {
                 continue;
             }
 
@@ -1156,36 +1175,41 @@
         String perm = checkPermissionAndRequest(request);
         checkPackageName(packageName);
 
-        if (mBlacklist.isBlacklisted(packageName)) {
-            if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
-                    packageName);
-            return null;
-        }
-
-        synchronized (mLock) {
-            // Figure out the provider. Either its explicitly request (deprecated API's),
-            // or use the fused provider
-            String name = request.getProvider();
-            if (name == null) name = LocationManager.FUSED_PROVIDER;
-            LocationProviderInterface provider = mProvidersByName.get(name);
-            if (provider == null) return null;
-
-            if (!isAllowedBySettingsLocked(name)) return null;
-
-            Location location = mLastLocation.get(name);
-            if (location == null) {
+        long identity = Binder.clearCallingIdentity();
+        try {
+            if (mBlacklist.isBlacklisted(packageName)) {
+                if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
+                        packageName);
                 return null;
             }
-            if (ACCESS_FINE_LOCATION.equals(perm)) {
-                return location;
-            } else {
-                Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
-                if (noGPSLocation != null) {
-                    return mLocationFudger.getOrCreate(noGPSLocation);
+
+            synchronized (mLock) {
+                // Figure out the provider. Either its explicitly request (deprecated API's),
+                // or use the fused provider
+                String name = request.getProvider();
+                if (name == null) name = LocationManager.FUSED_PROVIDER;
+                LocationProviderInterface provider = mProvidersByName.get(name);
+                if (provider == null) return null;
+
+                if (!isAllowedBySettingsLocked(name, mCurrentUserId)) return null;
+
+                Location location = mLastLocation.get(name);
+                if (location == null) {
+                    return null;
+                }
+                if (ACCESS_FINE_LOCATION.equals(perm)) {
+                    return location;
+                } else {
+                    Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
+                    if (noGPSLocation != null) {
+                        return mLocationFudger.getOrCreate(noGPSLocation);
+                    }
                 }
             }
+            return null;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-        return null;
     }
 
     @Override
@@ -1321,11 +1345,16 @@
                     "\" provider requires ACCESS_FINE_LOCATION permission");
         }
 
-        synchronized (mLock) {
-            LocationProviderInterface p = mProvidersByName.get(provider);
-            if (p == null) return false;
+        long identity = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                LocationProviderInterface p = mProvidersByName.get(provider);
+                if (p == null) return false;
 
-            return isAllowedBySettingsLocked(provider);
+                return isAllowedBySettingsLocked(provider, mCurrentUserId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
     }
 
@@ -1461,6 +1490,16 @@
             Receiver receiver = r.mReceiver;
             boolean receiverDead = false;
 
+            int receiverUserId = UserHandle.getUserId(receiver.mUid);
+            if (receiverUserId != mCurrentUserId) {
+                if (D) {
+                    Log.d(TAG, "skipping loc update for background user " + receiverUserId +
+                            " (current user: " + mCurrentUserId + ", app: " +
+                            receiver.mPackageName + ")");
+                }
+                continue;
+            }
+
             if (mBlacklist.isBlacklisted(receiver.mPackageName)) {
                 if (D) Log.d(TAG, "skipping loc update for blacklisted app: " +
                         receiver.mPackageName);
@@ -1551,7 +1590,7 @@
         }
 
         synchronized (mLock) {
-            if (isAllowedBySettingsLocked(provider)) {
+            if (isAllowedBySettingsLocked(provider, mCurrentUserId)) {
                 handleLocationChangedLocked(location, passive);
             }
         }
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 85b488c..93ae029 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
diff --git a/services/java/com/android/server/ServiceWatcher.java b/services/java/com/android/server/ServiceWatcher.java
index e99949b..5598b0a 100644
--- a/services/java/com/android/server/ServiceWatcher.java
+++ b/services/java/com/android/server/ServiceWatcher.java
@@ -27,6 +27,7 @@
 import android.content.pm.Signature;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.internal.content.PackageMonitor;
@@ -58,15 +59,17 @@
     private IBinder mBinder;   // connected service
     private String mPackageName;  // current best package
     private int mVersion;  // current best version
+    private int mCurrentUserId;
 
     public ServiceWatcher(Context context, String logTag, String action,
-            List<String> initialPackageNames, Runnable newServiceWork, Handler handler) {
+            List<String> initialPackageNames, Runnable newServiceWork, Handler handler, int userId) {
         mContext = context;
         mTag = logTag;
         mAction = action;
         mPm = mContext.getPackageManager();
         mNewServiceWork = newServiceWork;
         mHandler = handler;
+        mCurrentUserId = userId;
 
         mSignatureSets = new ArrayList<HashSet<Signature>>();
         for (int i=0; i < initialPackageNames.size(); i++) {
@@ -85,9 +88,11 @@
     }
 
     public boolean start() {
-        if (!bindBestPackage(null)) return false;
+        synchronized (mLock) {
+            if (!bindBestPackageLocked(null)) return false;
+        }
 
-        mPackageMonitor.register(mContext, null, true);
+        mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
         return true;
     }
 
@@ -98,13 +103,13 @@
      * is null.
      * Return true if a new package was found to bind to.
      */
-    private boolean bindBestPackage(String justCheckThisPackage) {
+    private boolean bindBestPackageLocked(String justCheckThisPackage) {
         Intent intent = new Intent(mAction);
         if (justCheckThisPackage != null) {
             intent.setPackage(justCheckThisPackage);
         }
-        List<ResolveInfo> rInfos = mPm.queryIntentServices(new Intent(mAction),
-                PackageManager.GET_META_DATA);
+        List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(new Intent(mAction),
+                PackageManager.GET_META_DATA, mCurrentUserId);
         int bestVersion = Integer.MIN_VALUE;
         String bestPackage = null;
         for (ResolveInfo rInfo : rInfos) {
@@ -141,36 +146,32 @@
                 (bestPackage == null ? "no new best package" : "new best packge: " + bestPackage)));
 
         if (bestPackage != null) {
-            bindToPackage(bestPackage, bestVersion);
+            bindToPackageLocked(bestPackage, bestVersion);
             return true;
         }
         return false;
     }
 
-    private void unbind() {
+    private void unbindLocked() {
         String pkg;
-        synchronized (mLock) {
-            pkg = mPackageName;
-            mPackageName = null;
-            mVersion = Integer.MIN_VALUE;
-        }
+        pkg = mPackageName;
+        mPackageName = null;
+        mVersion = Integer.MIN_VALUE;
         if (pkg != null) {
             if (D) Log.d(mTag, "unbinding " + pkg);
             mContext.unbindService(this);
         }
     }
 
-    private void bindToPackage(String packageName, int version) {
-        unbind();
+    private void bindToPackageLocked(String packageName, int version) {
+        unbindLocked();
         Intent intent = new Intent(mAction);
         intent.setPackage(packageName);
-        synchronized (mLock) {
-            mPackageName = packageName;
-            mVersion = version;
-        }
+        mPackageName = packageName;
+        mVersion = version;
         if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ")");
         mContext.bindService(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
-                | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE);
+                | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE, mCurrentUserId);
     }
 
     private boolean isSignatureMatch(Signature[] signatures) {
@@ -197,31 +198,37 @@
          */
         @Override
         public void onPackageUpdateFinished(String packageName, int uid) {
-            if (packageName.equals(mPackageName)) {
-                // package updated, make sure to rebind
-                unbind();
+            synchronized (mLock) {
+                if (packageName.equals(mPackageName)) {
+                    // package updated, make sure to rebind
+                    unbindLocked();
+                }
+                // check the updated package in case it is better
+                bindBestPackageLocked(packageName);
             }
-            // check the updated package in case it is better
-            bindBestPackage(packageName);
         }
 
         @Override
         public void onPackageAdded(String packageName, int uid) {
-            if (packageName.equals(mPackageName)) {
-                // package updated, make sure to rebind
-                unbind();
+            synchronized (mLock) {
+                if (packageName.equals(mPackageName)) {
+                    // package updated, make sure to rebind
+                    unbindLocked();
+                }
+                // check the new package is case it is better
+                bindBestPackageLocked(packageName);
             }
-            // check the new package is case it is better
-            bindBestPackage(packageName);
         }
 
         @Override
         public void onPackageRemoved(String packageName, int uid) {
-            if (packageName.equals(mPackageName)) {
-                unbind();
-                // the currently bound package was removed,
-                // need to search for a new package
-                bindBestPackage(null);
+            synchronized (mLock) {
+                if (packageName.equals(mPackageName)) {
+                    unbindLocked();
+                    // the currently bound package was removed,
+                    // need to search for a new package
+                    bindBestPackageLocked(null);
+                }
             }
         }
     };
@@ -271,4 +278,12 @@
             return mBinder;
         }
     }
+
+    public void switchUser(int userId) {
+        synchronized (mLock) {
+            unbindLocked();
+            mCurrentUserId = userId;
+            bindBestPackageLocked(null);
+        }
+    }
 }
diff --git a/services/java/com/android/server/WiredAccessoryManager.java b/services/java/com/android/server/WiredAccessoryManager.java
index 63e8895..d5c9c8f 100644
--- a/services/java/com/android/server/WiredAccessoryManager.java
+++ b/services/java/com/android/server/WiredAccessoryManager.java
@@ -152,7 +152,7 @@
                     break;
             }
 
-            updateLocked(NAME_H2W, headset);
+            updateLocked(NAME_H2W, (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC)) | headset);
         }
     }
 
diff --git a/services/java/com/android/server/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 c2aa3a5..daed0a2 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3585,7 +3585,7 @@
                         Slog.w(TAG, "Failed trying to unstop package "
                                 + packageName + ": " + e);
                     }
-                    if (isUserRunningLocked(user)) {
+                    if (isUserRunningLocked(user, false)) {
                         forceStopPackageLocked(packageName, pkgUid);
                     }
                 }
@@ -3739,7 +3739,8 @@
     private void forceStopUserLocked(int userId) {
         forceStopPackageLocked(null, -1, false, false, true, false, userId);
         Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                | Intent.FLAG_RECEIVER_FOREGROUND);
         intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
         broadcastIntentLocked(null, null, intent,
                 null, null, 0, null, null, null,
@@ -7904,6 +7905,19 @@
                 broadcastIntentLocked(null, null, intent,
                         null, null, 0, null, null, null,
                         false, false, MY_PID, Process.SYSTEM_UID, mCurrentUserId);
+                intent = new Intent(Intent.ACTION_USER_STARTING);
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId);
+                broadcastIntentLocked(null, null, intent,
+                        null, new IIntentReceiver.Stub() {
+                            @Override
+                            public void performReceive(Intent intent, int resultCode, String data,
+                                    Bundle extras, boolean ordered, boolean sticky, int sendingUser)
+                                    throws RemoteException {
+                            }
+                        }, 0, null, null,
+                        android.Manifest.permission.INTERACT_ACROSS_USERS,
+                        false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -8879,7 +8893,7 @@
                 pw.println("  [-a] [-c] [-h] [cmd] ...");
                 pw.println("  cmd may be one of:");
                 pw.println("    a[ctivities]: activity stack state");
-                pw.println("    b[roadcasts] [PACKAGE_NAME]: broadcast state");
+                pw.println("    b[roadcasts] [PACKAGE_NAME] [history [-s]]: broadcast state");
                 pw.println("    i[ntents] [PACKAGE_NAME]: pending intent state");
                 pw.println("    p[rocesses] [PACKAGE_NAME]: process state");
                 pw.println("    o[om]: out of memory management");
@@ -9338,6 +9352,12 @@
             pw.print("    User #"); pw.print(uss.mHandle.getIdentifier());
                     pw.print(": "); uss.dump("", pw);
         }
+        pw.print("  mStartedUserArray: [");
+        for (int i=0; i<mStartedUserArray.length; i++) {
+            if (i > 0) pw.print(", ");
+            pw.print(mStartedUserArray[i]);
+        }
+        pw.println("]");
         pw.print("  mUserLru: [");
         for (int i=0; i<mUserLru.size(); i++) {
             if (i > 0) pw.print(", ");
@@ -9707,6 +9727,9 @@
         boolean onlyHistory = false;
 
         if ("history".equals(dumpPackage)) {
+            if (opti < args.length && "-s".equals(args[opti])) {
+                dumpAll = false;
+            }
             onlyHistory = true;
             dumpPackage = null;
         }
@@ -14106,11 +14129,14 @@
                 mWindowManager.startFreezingScreen(R.anim.screen_user_exit,
                         R.anim.screen_user_enter);
 
+                boolean needStart = false;
+
                 // If the user we are switching to is not currently started, then
                 // we need to start it now.
                 if (mStartedUsers.get(userId) == null) {
                     mStartedUsers.put(userId, new UserStartedState(new UserHandle(userId), false));
                     updateStartedUserArrayLocked();
+                    needStart = true;
                 }
 
                 mCurrentUserId = userId;
@@ -14134,10 +14160,14 @@
                     // so we can just fairly silently bring the user back from
                     // the almost-dead.
                     uss.mState = UserStartedState.STATE_RUNNING;
+                    updateStartedUserArrayLocked();
+                    needStart = true;
                 } else if (uss.mState == UserStartedState.STATE_SHUTDOWN) {
                     // This means ACTION_SHUTDOWN has been sent, so we will
                     // need to treat this as a new boot of the user.
                     uss.mState = UserStartedState.STATE_BOOTING;
+                    updateStartedUserArrayLocked();
+                    needStart = true;
                 }
 
                 mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
@@ -14146,17 +14176,19 @@
                         oldUserId, userId, uss));
                 mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
                         oldUserId, userId, uss), USER_SWITCH_TIMEOUT);
-                Intent intent = new Intent(Intent.ACTION_USER_STARTED);
-                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
-                        | Intent.FLAG_RECEIVER_FOREGROUND);
-                intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-                broadcastIntentLocked(null, null, intent,
-                        null, null, 0, null, null, null,
-                        false, false, MY_PID, Process.SYSTEM_UID, userId);
+                if (needStart) {
+                    Intent intent = new Intent(Intent.ACTION_USER_STARTED);
+                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                            | Intent.FLAG_RECEIVER_FOREGROUND);
+                    intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+                    broadcastIntentLocked(null, null, intent,
+                            null, null, 0, null, null, null,
+                            false, false, MY_PID, Process.SYSTEM_UID, userId);
+                }
 
                 if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
                     if (userId != 0) {
-                        intent = new Intent(Intent.ACTION_USER_INITIALIZE);
+                        Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
                         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                         broadcastIntentLocked(null, null, intent, null,
                                 new IIntentReceiver.Stub() {
@@ -14180,6 +14212,21 @@
 
                 getUserManagerLocked().userForeground(userId);
                 sendUserSwitchBroadcastsLocked(oldUserId, userId);
+                if (needStart) {
+                    Intent intent = new Intent(Intent.ACTION_USER_STARTING);
+                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+                    broadcastIntentLocked(null, null, intent,
+                            null, new IIntentReceiver.Stub() {
+                                @Override
+                                public void performReceive(Intent intent, int resultCode, String data,
+                                        Bundle extras, boolean ordered, boolean sticky, int sendingUser)
+                                        throws RemoteException {
+                                }
+                            }, 0, null, null,
+                            android.Manifest.permission.INTERACT_ACROSS_USERS,
+                            false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -14217,19 +14264,6 @@
                         null, null, 0, null, null,
                         android.Manifest.permission.MANAGE_USERS,
                         false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
-                intent = new Intent(Intent.ACTION_USER_STARTING);
-                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-                intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
-                broadcastIntentLocked(null, null, intent,
-                        null, new IIntentReceiver.Stub() {
-                            @Override
-                            public void performReceive(Intent intent, int resultCode, String data,
-                                    Bundle extras, boolean ordered, boolean sticky, int sendingUser)
-                                    throws RemoteException {
-                            }
-                        }, 0, null, null,
-                        android.Manifest.permission.INTERACT_ACROSS_USERS,
-                        false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -14318,8 +14352,7 @@
 
     void finishUserSwitch(UserStartedState uss) {
         synchronized (this) {
-            if ((uss.mState == UserStartedState.STATE_BOOTING
-                    || uss.mState == UserStartedState.STATE_SHUTDOWN)
+            if (uss.mState == UserStartedState.STATE_BOOTING
                     && mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) {
                 uss.mState = UserStartedState.STATE_RUNNING;
                 final int userId = uss.mHandle.getIdentifier();
@@ -14410,6 +14443,7 @@
         if (uss.mState != UserStartedState.STATE_STOPPING
                 && uss.mState != UserStartedState.STATE_SHUTDOWN) {
             uss.mState = UserStartedState.STATE_STOPPING;
+            updateStartedUserArrayLocked();
 
             long ident = Binder.clearCallingIdentity();
             try {
@@ -14514,7 +14548,7 @@
     }
 
     @Override
-    public boolean isUserRunning(int userId) {
+    public boolean isUserRunning(int userId, boolean orStopped) {
         if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
                 != PackageManager.PERMISSION_GRANTED) {
             String msg = "Permission Denial: isUserRunning() from pid="
@@ -14525,13 +14559,19 @@
             throw new SecurityException(msg);
         }
         synchronized (this) {
-            return isUserRunningLocked(userId);
+            return isUserRunningLocked(userId, orStopped);
         }
     }
 
-    boolean isUserRunningLocked(int userId) {
+    boolean isUserRunningLocked(int userId, boolean orStopped) {
         UserStartedState state = mStartedUsers.get(userId);
-        return state != null && state.mState != UserStartedState.STATE_STOPPING
+        if (state == null) {
+            return false;
+        }
+        if (orStopped) {
+            return true;
+        }
+        return state.mState != UserStartedState.STATE_STOPPING
                 && state.mState != UserStartedState.STATE_SHUTDOWN;
     }
 
@@ -14552,9 +14592,24 @@
     }
 
     private void updateStartedUserArrayLocked() {
-        mStartedUserArray = new int[mStartedUsers.size()];
+        int num = 0;
         for (int i=0; i<mStartedUsers.size();  i++) {
-            mStartedUserArray[i] = mStartedUsers.keyAt(i);
+            UserStartedState uss = mStartedUsers.valueAt(i);
+            // This list does not include stopping users.
+            if (uss.mState != UserStartedState.STATE_STOPPING
+                    && uss.mState != UserStartedState.STATE_SHUTDOWN) {
+                num++;
+            }
+        }
+        mStartedUserArray = new int[num];
+        num = 0;
+        for (int i=0; i<mStartedUsers.size();  i++) {
+            UserStartedState uss = mStartedUsers.valueAt(i);
+            if (uss.mState != UserStartedState.STATE_STOPPING
+                    && uss.mState != UserStartedState.STATE_SHUTDOWN) {
+                mStartedUserArray[num] = mStartedUsers.keyAt(i);
+                num++;
+            }
         }
     }
 
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index 95c22ec..f9630ae 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -54,6 +54,7 @@
     static final boolean DEBUG_MU = ActivityManagerService.DEBUG_MU;
 
     static final int MAX_BROADCAST_HISTORY = 25;
+    static final int MAX_BROADCAST_SUMMARY_HISTORY = 100;
 
     final ActivityManagerService mService;
 
@@ -93,6 +94,12 @@
             = new BroadcastRecord[MAX_BROADCAST_HISTORY];
 
     /**
+     * Summary of historical data of past broadcasts, for debugging.
+     */
+    final Intent[] mBroadcastSummaryHistory
+            = new Intent[MAX_BROADCAST_SUMMARY_HISTORY];
+
+    /**
      * Set when we current have a BROADCAST_INTENT_MSG in flight.
      */
     boolean mBroadcastsScheduled = false;
@@ -922,6 +929,9 @@
                 MAX_BROADCAST_HISTORY-1);
         r.finishTime = SystemClock.uptimeMillis();
         mBroadcastHistory[0] = r;
+        System.arraycopy(mBroadcastSummaryHistory, 0, mBroadcastSummaryHistory, 1,
+                MAX_BROADCAST_SUMMARY_HISTORY-1);
+        mBroadcastSummaryHistory[0] = r.intent;
     }
 
     final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
@@ -1006,8 +1016,9 @@
             }
         }
 
+        int i;
         boolean printed = false;
-        for (int i=0; i<MAX_BROADCAST_HISTORY; i++) {
+        for (i=0; i<MAX_BROADCAST_HISTORY; i++) {
             BroadcastRecord r = mBroadcastHistory[i];
             if (r == null) {
                 break;
@@ -1028,11 +1039,44 @@
                         pw.print(i); pw.println(":");
                 r.dump(pw, "    ");
             } else {
-                if (i >= 50) {
+                pw.print("  #"); pw.print(i); pw.print(": "); pw.println(r);
+                pw.print("    ");
+                pw.println(r.intent.toShortString(false, true, true, false));
+                Bundle bundle = r.intent.getExtras();
+                if (bundle != null) {
+                    pw.print("    extras: "); pw.println(bundle.toString());
+                }
+            }
+        }
+
+        if (dumpPackage == null) {
+            if (dumpAll) {
+                i = 0;
+                printed = false;
+            }
+            for (; i<MAX_BROADCAST_SUMMARY_HISTORY; i++) {
+                Intent intent = mBroadcastSummaryHistory[i];
+                if (intent == null) {
+                    break;
+                }
+                if (!printed) {
+                    if (needSep) {
+                        pw.println();
+                    }
+                    needSep = true;
+                    pw.println("  Historical broadcasts summary [" + mQueueName + "]:");
+                    printed = true;
+                }
+                if (!dumpAll && i >= 50) {
                     pw.println("  ...");
                     break;
                 }
-                pw.print("  #"); pw.print(i); pw.print(": "); pw.println(r);
+                pw.print("  #"); pw.print(i); pw.print(": ");
+                pw.println(intent.toShortString(false, true, true, false));
+                Bundle bundle = intent.getExtras();
+                if (bundle != null) {
+                    pw.print("    extras: "); pw.println(bundle.toString());
+                }
             }
         }
 
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index 85ec328..1cf5b9c 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -81,12 +81,10 @@
         final long now = SystemClock.uptimeMillis();
 
         pw.print(prefix); pw.print(this); pw.print(" to user "); pw.println(userId);
-        pw.print(prefix); pw.println(intent);
-        if (sticky) {
-            Bundle bundle = intent.getExtras();
-            if (bundle != null) {
-                pw.print(prefix); pw.print("extras: "); pw.println(bundle.toString());
-            }
+        pw.print(prefix); pw.println(intent.toInsecureString());
+        Bundle bundle = intent.getExtras();
+        if (bundle != null) {
+            pw.print(prefix); pw.print("extras: "); pw.println(bundle.toString());
         }
         pw.print(prefix); pw.print("caller="); pw.print(callerPackage); pw.print(" ");
                 pw.print(callerApp != null ? callerApp.toShortString() : "null");
diff --git a/services/java/com/android/server/display/DisplayDevice.java b/services/java/com/android/server/display/DisplayDevice.java
index f5aa3d4..a3ab3c1 100644
--- a/services/java/com/android/server/display/DisplayDevice.java
+++ b/services/java/com/android/server/display/DisplayDevice.java
@@ -105,6 +105,18 @@
     }
 
     /**
+     * Blanks the display, if supported.
+     */
+    public void blankLocked() {
+    }
+
+    /**
+     * Unblanks the display, if supported.
+     */
+    public void unblankLocked() {
+    }
+
+    /**
      * Sets the display layer stack while in a transaction.
      */
     public final void setLayerStackInTransactionLocked(int layerStack) {
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index b8c6cd5..0a42528 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -103,6 +103,10 @@
     private static final int MSG_REQUEST_TRAVERSAL = 4;
     private static final int MSG_UPDATE_VIEWPORT = 5;
 
+    private static final int DISPLAY_BLANK_STATE_UNKNOWN = 0;
+    private static final int DISPLAY_BLANK_STATE_BLANKED = 1;
+    private static final int DISPLAY_BLANK_STATE_UNBLANKED = 2;
+
     private final Context mContext;
     private final boolean mHeadless;
     private final DisplayManagerHandler mHandler;
@@ -141,6 +145,9 @@
             new SparseArray<LogicalDisplay>();
     private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1;
 
+    // Set to true if all displays have been blanked by the power manager.
+    private int mAllDisplayBlankStateFromPowerManager;
+
     // Set to true when there are pending display changes that have yet to be applied
     // to the surface flinger state.
     private boolean mPendingTraversal;
@@ -286,6 +293,40 @@
     }
 
     /**
+     * Called by the power manager to blank all displays.
+     */
+    public void blankAllDisplaysFromPowerManager() {
+        synchronized (mSyncRoot) {
+            if (mAllDisplayBlankStateFromPowerManager != DISPLAY_BLANK_STATE_BLANKED) {
+                mAllDisplayBlankStateFromPowerManager = DISPLAY_BLANK_STATE_BLANKED;
+
+                final int count = mDisplayDevices.size();
+                for (int i = 0; i < count; i++) {
+                    DisplayDevice device = mDisplayDevices.get(i);
+                    device.blankLocked();
+                }
+            }
+        }
+    }
+
+    /**
+     * Called by the power manager to unblank all displays.
+     */
+    public void unblankAllDisplaysFromPowerManager() {
+        synchronized (mSyncRoot) {
+            if (mAllDisplayBlankStateFromPowerManager != DISPLAY_BLANK_STATE_UNBLANKED) {
+                mAllDisplayBlankStateFromPowerManager = DISPLAY_BLANK_STATE_UNBLANKED;
+
+                final int count = mDisplayDevices.size();
+                for (int i = 0; i < count; i++) {
+                    DisplayDevice device = mDisplayDevices.get(i);
+                    device.unblankLocked();
+                }
+            }
+        }
+    }
+
+    /**
      * Returns information about the specified logical display.
      *
      * @param displayId The logical display id.
@@ -528,6 +569,17 @@
             mDisplayDevices.add(device);
             addLogicalDisplayLocked(device);
             scheduleTraversalLocked(false);
+
+            // Blank or unblank the display immediately to match the state requested
+            // by the power manager (if known).
+            switch (mAllDisplayBlankStateFromPowerManager) {
+                case DISPLAY_BLANK_STATE_BLANKED:
+                    device.blankLocked();
+                    break;
+                case DISPLAY_BLANK_STATE_UNBLANKED:
+                    device.unblankLocked();
+                    break;
+            }
         }
     }
 
@@ -788,9 +840,18 @@
         }
 
         pw.println("DISPLAY MANAGER (dumpsys display)");
-        pw.println("  mHeadless=" + mHeadless);
 
         synchronized (mSyncRoot) {
+            pw.println("  mHeadless=" + mHeadless);
+            pw.println("  mOnlyCode=" + mOnlyCore);
+            pw.println("  mSafeMode=" + mSafeMode);
+            pw.println("  mPendingTraversal=" + mPendingTraversal);
+            pw.println("  mAllDisplayBlankStateFromPowerManager="
+                    + mAllDisplayBlankStateFromPowerManager);
+            pw.println("  mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId);
+            pw.println("  mDefaultViewport=" + mDefaultViewport);
+            pw.println("  mExternalTouchViewport=" + mExternalTouchViewport);
+
             IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
             ipw.increaseIndent();
 
@@ -817,10 +878,6 @@
                 pw.println("  Display " + displayId + ":");
                 display.dumpLocked(ipw);
             }
-
-            pw.println();
-            pw.println("Default viewport: " + mDefaultViewport);
-            pw.println("External touch viewport: " + mExternalTouchViewport);
         }
     }
 
diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java
index 679a67e..d780006 100644
--- a/services/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/java/com/android/server/display/LocalDisplayAdapter.java
@@ -92,6 +92,7 @@
 
         private DisplayDeviceInfo mInfo;
         private boolean mHavePendingChanges;
+        private boolean mBlanked;
 
         public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId,
                 PhysicalDisplayInfo phys) {
@@ -150,10 +151,23 @@
         }
 
         @Override
+        public void blankLocked() {
+            mBlanked = true;
+            Surface.blankDisplay(getDisplayTokenLocked());
+        }
+
+        @Override
+        public void unblankLocked() {
+            mBlanked = false;
+            Surface.unblankDisplay(getDisplayTokenLocked());
+        }
+
+        @Override
         public void dumpLocked(PrintWriter pw) {
             super.dumpLocked(pw);
             pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId);
             pw.println("mPhys=" + mPhys);
+            pw.println("mBlanked=" + mBlanked);
         }
     }
 
diff --git a/services/java/com/android/server/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java
index 7d030e9..f5cc59f 100644
--- a/services/java/com/android/server/location/GeocoderProxy.java
+++ b/services/java/com/android/server/location/GeocoderProxy.java
@@ -21,6 +21,7 @@
 import android.location.GeocoderParams;
 import android.location.IGeocodeProvider;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.server.ServiceWatcher;
@@ -38,8 +39,8 @@
     private final ServiceWatcher mServiceWatcher;
 
     public static GeocoderProxy createAndBind(Context context,
-            List<String> initialPackageNames) {
-        GeocoderProxy proxy = new GeocoderProxy(context, initialPackageNames);
+            List<String> initialPackageNames, int userId) {
+        GeocoderProxy proxy = new GeocoderProxy(context, initialPackageNames, userId);
         if (proxy.bind()) {
             return proxy;
         } else {
@@ -47,11 +48,11 @@
         }
     }
 
-    public GeocoderProxy(Context context, List<String> initialPackageNames) {
+    public GeocoderProxy(Context context, List<String> initialPackageNames, int userId) {
         mContext = context;
 
         mServiceWatcher = new ServiceWatcher(mContext, TAG, SERVICE_ACTION, initialPackageNames,
-                null, null);
+                null, null, userId);
     }
 
     private boolean bind () {
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index a254d74..c272da4 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -783,6 +783,11 @@
         sendMessage(SET_REQUEST, 0, new GpsRequest(request, source));
     }
 
+    @Override
+    public void switchUser(int userId) {
+        // nothing to do here
+    }
+
     private void handleSetRequest(ProviderRequest request, WorkSource source) {
         if (DEBUG) Log.d(TAG, "setRequest " + request);
 
diff --git a/services/java/com/android/server/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java
index 6f09232..80e71f1 100644
--- a/services/java/com/android/server/location/LocationProviderInterface.java
+++ b/services/java/com/android/server/location/LocationProviderInterface.java
@@ -38,6 +38,8 @@
     public boolean isEnabled();
     public void setRequest(ProviderRequest request, WorkSource source);
 
+    public void switchUser(int userId);
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args);
 
     // --- deprecated (but still supported) ---
diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
index 7faf72c..dd2e71c 100644
--- a/services/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -25,6 +25,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.os.WorkSource;
 import android.util.Log;
 
@@ -54,9 +55,9 @@
     private WorkSource mWorksource = new WorkSource();
 
     public static LocationProviderProxy createAndBind(Context context, String name, String action,
-            List<String> initialPackageNames, Handler handler) {
+            List<String> initialPackageNames, Handler handler, int userId) {
         LocationProviderProxy proxy = new LocationProviderProxy(context, name, action,
-                initialPackageNames, handler);
+                initialPackageNames, handler, userId);
         if (proxy.bind()) {
             return proxy;
         } else {
@@ -65,11 +66,11 @@
     }
 
     private LocationProviderProxy(Context context, String name, String action,
-            List<String> initialPackageNames, Handler handler) {
+            List<String> initialPackageNames, Handler handler, int userId) {
         mContext = context;
         mName = name;
         mServiceWatcher = new ServiceWatcher(mContext, TAG, action, initialPackageNames,
-                mNewServiceWork, handler);
+                mNewServiceWork, handler, userId);
     }
 
     private boolean bind () {
@@ -211,6 +212,11 @@
     }
 
     @Override
+    public void switchUser(int userId) {
+        mServiceWatcher.switchUser(userId);
+    }
+
+    @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.append("REMOTE SERVICE");
         pw.append(" name=").append(mName);
diff --git a/services/java/com/android/server/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java
index 36c43ff..1194cbc 100644
--- a/services/java/com/android/server/location/MockProvider.java
+++ b/services/java/com/android/server/location/MockProvider.java
@@ -156,6 +156,11 @@
     public void setRequest(ProviderRequest request, WorkSource source) { }
 
     @Override
+    public void switchUser(int userId) {
+        // nothing to do here
+    }
+
+    @Override
     public boolean sendExtraCommand(String command, Bundle extras) {
         return false;
     }
diff --git a/services/java/com/android/server/location/PassiveProvider.java b/services/java/com/android/server/location/PassiveProvider.java
index 71bae07..734c572 100644
--- a/services/java/com/android/server/location/PassiveProvider.java
+++ b/services/java/com/android/server/location/PassiveProvider.java
@@ -96,6 +96,11 @@
         mReportLocation = request.reportLocation;
     }
 
+    @Override
+    public void switchUser(int userId) {
+        // nothing to do here
+    }
+
     public void updateLocation(Location location) {
         if (mReportLocation) {
             try {
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 0600f5c..b8d7286 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -6374,12 +6374,22 @@
             mArgs = args;
 
             if (ret == PackageManager.INSTALL_SUCCEEDED) {
+                 /*
+                 * ADB installs appear as UserHandle.USER_ALL, and can only be performed by
+                 * UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER.
+                 */
+                int userIdentifier = getUser().getIdentifier();
+                if (userIdentifier == UserHandle.USER_ALL
+                        && ((flags & PackageManager.INSTALL_FROM_ADB) != 0)) {
+                    userIdentifier = UserHandle.USER_OWNER;
+                }
+
                 /*
                  * Determine if we have any installed package verifiers. If we
                  * do, then we'll defer to them to verify the packages.
                  */
                 final int requiredUid = mRequiredVerifierPackage == null ? -1
-                        : getPackageUid(mRequiredVerifierPackage, getUser().getIdentifier());
+                        : getPackageUid(mRequiredVerifierPackage, userIdentifier);
                 if (requiredUid != -1 && isVerificationEnabled(flags)) {
                     final Intent verification = new Intent(
                             Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
diff --git a/services/java/com/android/server/power/DisplayBlanker.java b/services/java/com/android/server/power/DisplayBlanker.java
new file mode 100644
index 0000000..6072053
--- /dev/null
+++ b/services/java/com/android/server/power/DisplayBlanker.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+/**
+ * Blanks or unblanks all displays.
+ */
+interface DisplayBlanker {
+    public void blankAllDisplays();
+    public void unblankAllDisplays();
+}
diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java
index 82c3617..6a57372 100644
--- a/services/java/com/android/server/power/DisplayPowerController.java
+++ b/services/java/com/android/server/power/DisplayPowerController.java
@@ -159,6 +159,9 @@
     // A suspend blocker.
     private final SuspendBlocker mSuspendBlocker;
 
+    // The display blanker.
+    private final DisplayBlanker mDisplayBlanker;
+
     // Our handler.
     private final DisplayControllerHandler mHandler;
 
@@ -343,10 +346,12 @@
      */
     public DisplayPowerController(Looper looper, Context context, Notifier notifier,
             LightsService lights, TwilightService twilight, SuspendBlocker suspendBlocker,
+            DisplayBlanker displayBlanker,
             Callbacks callbacks, Handler callbackHandler) {
         mHandler = new DisplayControllerHandler(looper);
         mNotifier = notifier;
         mSuspendBlocker = suspendBlocker;
+        mDisplayBlanker = displayBlanker;
         mCallbacks = callbacks;
         mCallbackHandler = callbackHandler;
 
@@ -520,7 +525,8 @@
                 new ElectronBeam(display),
                 new PhotonicModulator(executor,
                         mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT),
-                        mSuspendBlocker));
+                        mSuspendBlocker),
+                mDisplayBlanker);
 
         mElectronBeamOnAnimator = ObjectAnimator.ofFloat(
                 mPowerState, DisplayPowerState.ELECTRON_BEAM_LEVEL, 0.0f, 1.0f);
diff --git a/services/java/com/android/server/power/DisplayPowerState.java b/services/java/com/android/server/power/DisplayPowerState.java
index f6ce7a7..fdfcacc 100644
--- a/services/java/com/android/server/power/DisplayPowerState.java
+++ b/services/java/com/android/server/power/DisplayPowerState.java
@@ -51,7 +51,8 @@
 
     private final Choreographer mChoreographer;
     private final ElectronBeam mElectronBeam;
-    private final PhotonicModulator mScreenBrightnessModulator;
+    private final PhotonicModulator mPhotonicModulator;
+    private final DisplayBlanker mDisplayBlanker;
 
     private int mDirty;
     private boolean mScreenOn;
@@ -61,10 +62,11 @@
     private Runnable mCleanListener;
 
     public DisplayPowerState(ElectronBeam electronBean,
-            PhotonicModulator screenBrightnessModulator) {
+            PhotonicModulator photonicModulator, DisplayBlanker displayBlanker) {
         mChoreographer = Choreographer.getInstance();
         mElectronBeam = electronBean;
-        mScreenBrightnessModulator = screenBrightnessModulator;
+        mPhotonicModulator = photonicModulator;
+        mDisplayBlanker = displayBlanker;
 
         // At boot time, we know that the screen is on and the electron beam
         // animation is not playing.  We don't know the screen's brightness though,
@@ -238,8 +240,8 @@
     private void apply() {
         if (mDirty != 0) {
             if ((mDirty & DIRTY_SCREEN_ON) != 0 && !mScreenOn) {
-                mScreenBrightnessModulator.setBrightness(0, true /*sync*/);
-                PowerManagerService.nativeSetScreenState(false);
+                mPhotonicModulator.setBrightness(0, true /*sync*/);
+                mDisplayBlanker.blankAllDisplays();
             }
 
             if ((mDirty & DIRTY_ELECTRON_BEAM) != 0) {
@@ -247,12 +249,12 @@
             }
 
             if ((mDirty & DIRTY_SCREEN_ON) != 0 && mScreenOn) {
-                PowerManagerService.nativeSetScreenState(true);
+                mDisplayBlanker.unblankAllDisplays();
             }
 
             if ((mDirty & (DIRTY_BRIGHTNESS | DIRTY_SCREEN_ON | DIRTY_ELECTRON_BEAM)) != 0
                     && mScreenOn) {
-                mScreenBrightnessModulator.setBrightness(
+                mPhotonicModulator.setBrightness(
                         (int)(mScreenBrightness * mElectronBeamLevel), false /*sync*/);
             }
 
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index abbae5b..b76ad45 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -48,6 +48,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.SystemService;
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.provider.Settings;
@@ -81,6 +82,8 @@
     private static final int MSG_SANDMAN = 2;
     // Message: Sent when the screen on blocker is released.
     private static final int MSG_SCREEN_ON_BLOCKER_RELEASED = 3;
+    // Message: Sent to poll whether the boot animation has terminated.
+    private static final int MSG_CHECK_IF_BOOT_ANIMATION_FINISHED = 4;
 
     // Dirty bit: mWakeLocks changed
     private static final int DIRTY_WAKE_LOCKS = 1 << 0;
@@ -127,6 +130,7 @@
     private static final int WAKE_LOCK_SCREEN_DIM = 1 << 2;
     private static final int WAKE_LOCK_BUTTON_BRIGHT = 1 << 3;
     private static final int WAKE_LOCK_PROXIMITY_SCREEN_OFF = 1 << 4;
+    private static final int WAKE_LOCK_STAY_AWAKE = 1 << 5; // only set if already awake
 
     // Summarizes the user activity state.
     private static final int USER_ACTIVITY_SCREEN_BRIGHT = 1 << 0;
@@ -152,9 +156,16 @@
     // 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;
+    private DisplayManagerService mDisplayManagerService;
     private IBatteryStats mBatteryStats;
     private HandlerThread mHandlerThread;
     private PowerManagerHandler mHandler;
@@ -230,6 +241,9 @@
     // screen is coming up.
     private final ScreenOnBlockerImpl mScreenOnBlocker;
 
+    // The display blanker used to turn the screen on or off.
+    private final DisplayBlankerImpl mDisplayBlanker;
+
     // True if systemReady() has been called.
     private boolean mSystemReady;
 
@@ -319,14 +333,15 @@
     private static native void nativeSetPowerState(boolean screenOn, boolean screenBright);
     private static native void nativeAcquireSuspendBlocker(String name);
     private static native void nativeReleaseSuspendBlocker(String name);
-
-    static native void nativeSetScreenState(boolean on);
+    private static native void nativeSetInteractive(boolean enable);
+    private static native void nativeSetAutoSuspend(boolean enable);
 
     public PowerManagerService() {
         synchronized (mLock) {
             mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService");
             mWakeLockSuspendBlocker.acquire();
             mScreenOnBlocker = new ScreenOnBlockerImpl();
+            mDisplayBlanker = new DisplayBlankerImpl();
             mHoldingWakeLockSuspendBlocker = true;
             mWakefulness = WAKEFULNESS_AWAKE;
         }
@@ -342,23 +357,24 @@
     public void init(Context context, LightsService ls,
             ActivityManagerService am, BatteryService bs, IBatteryStats bss,
             DisplayManagerService dm) {
+        mContext = context;
+        mLightsService = ls;
+        mBatteryService = bs;
+        mBatteryStats = bss;
+        mDisplayManagerService = dm;
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
+
+        Watchdog.getInstance().addMonitor(this);
+
         // Forcibly turn the screen on at boot so that it is in a known power state.
         // We do this in init() rather than in the constructor because setting the
         // screen state requires a call into surface flinger which then needs to call back
         // into the activity manager to check permissions.  Unfortunately the
         // activity manager is not running when the constructor is called, so we
         // have to defer setting the screen state until this point.
-        nativeSetScreenState(true);
-
-        mContext = context;
-        mLightsService = ls;
-        mBatteryService = bs;
-        mBatteryStats = bss;
-        mHandlerThread = new HandlerThread(TAG);
-        mHandlerThread.start();
-        mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
-
-        Watchdog.getInstance().addMonitor(this);
+        mDisplayBlanker.unblankAllDisplays();
     }
 
     public void setPolicy(WindowManagerPolicy policy) {
@@ -388,7 +404,7 @@
             mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(),
                     mContext, mNotifier, mLightsService, twilight,
                     createSuspendBlockerLocked("PowerManagerService.Display"),
-                    mDisplayPowerControllerCallbacks, mHandler);
+                    mDisplayBlanker, mDisplayPowerControllerCallbacks, mHandler);
 
             mSettingsObserver = new SettingsObserver(mHandler);
             mAttentionLight = mLightsService.getLight(LightsService.LIGHT_ID_ATTENTION);
@@ -1166,16 +1182,25 @@
                         if (mWakefulness != WAKEFULNESS_ASLEEP) {
                             mWakeLockSummary |= WAKE_LOCK_CPU
                                     | WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT;
+                            if (mWakefulness == WAKEFULNESS_AWAKE) {
+                                mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE;
+                            }
                         }
                         break;
                     case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
                         if (mWakefulness != WAKEFULNESS_ASLEEP) {
                             mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_BRIGHT;
+                            if (mWakefulness == WAKEFULNESS_AWAKE) {
+                                mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE;
+                            }
                         }
                         break;
                     case PowerManager.SCREEN_DIM_WAKE_LOCK:
                         if (mWakefulness != WAKEFULNESS_ASLEEP) {
                             mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_DIM;
+                            if (mWakefulness == WAKEFULNESS_AWAKE) {
+                                mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE;
+                            }
                         }
                         break;
                     case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
@@ -1333,7 +1358,7 @@
     private boolean isBeingKeptAwakeLocked() {
         return mStayOn
                 || mProximityPositive
-                || (mWakeLockSummary & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) != 0
+                || (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0
                 || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
                         | USER_ACTIVITY_SCREEN_DIM)) != 0;
     }
@@ -1646,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;
@@ -2106,6 +2154,9 @@
             pw.println();
             pw.println("Screen On Blocker: " + mScreenOnBlocker);
 
+            pw.println();
+            pw.println("Display Blanker: " + mDisplayBlanker);
+
             dpc = mDisplayPowerController;
         }
 
@@ -2151,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();
         }
     }
 
@@ -2208,6 +2263,9 @@
                 case MSG_SCREEN_ON_BLOCKER_RELEASED:
                     handleScreenOnBlockerReleased();
                     break;
+                case MSG_CHECK_IF_BOOT_ANIMATION_FINISHED:
+                    checkIfBootAnimationFinished();
+                    break;
             }
         }
     }
@@ -2397,5 +2455,36 @@
                 return "held=" + (mNestCount != 0) + ", mNestCount=" + mNestCount;
             }
         }
-    };
+    }
+
+    private final class DisplayBlankerImpl implements DisplayBlanker {
+        private boolean mBlanked;
+
+        @Override
+        public void blankAllDisplays() {
+            synchronized (this) {
+                mBlanked = true;
+                mDisplayManagerService.blankAllDisplaysFromPowerManager();
+                nativeSetInteractive(false);
+                nativeSetAutoSuspend(true);
+            }
+        }
+
+        @Override
+        public void unblankAllDisplays() {
+            synchronized (this) {
+                nativeSetAutoSuspend(false);
+                nativeSetInteractive(true);
+                mDisplayManagerService.unblankAllDisplaysFromPowerManager();
+                mBlanked = false;
+            }
+        }
+
+        @Override
+        public String toString() {
+            synchronized (this) {
+                return "blanked=" + mBlanked;
+            }
+        }
+    }
 }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 0db9f19..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.
@@ -5651,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
@@ -6021,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);
@@ -7646,7 +7650,7 @@
                 }
 
                 case SHOW_STRICT_MODE_VIOLATION: {
-                    showStrictModeViolation(msg.arg1);
+                    showStrictModeViolation(msg.arg1, msg.arg2);
                     break;
                 }
 
@@ -8383,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);
             }
         }
     }
diff --git a/services/jni/com_android_server_power_PowerManagerService.cpp b/services/jni/com_android_server_power_PowerManagerService.cpp
index dcc2b58..23c33af 100644
--- a/services/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/jni/com_android_server_power_PowerManagerService.cpp
@@ -26,7 +26,6 @@
 #include <limits.h>
 
 #include <android_runtime/AndroidRuntime.h>
-#include <gui/ISurfaceComposer.h>
 #include <utils/Timers.h>
 #include <utils/misc.h>
 #include <utils/String8.h>
@@ -36,8 +35,6 @@
 #include <cutils/android_reboot.h>
 #include <suspend/autosuspend.h>
 
-#include <private/gui/ComposerService.h>
-
 #include "com_android_server_power_PowerManagerService.h"
 
 namespace android {
@@ -170,40 +167,25 @@
     release_wake_lock(name.c_str());
 }
 
-static void nativeSetScreenState(JNIEnv *env, jclass clazz, jboolean on) {
-    sp<ISurfaceComposer> s(ComposerService::getComposerService());
-    if (on) {
-        {
-            ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_disable() while turning screen on");
-            autosuspend_disable();
-        }
-
-        if (gPowerModule) {
+static void nativeSetInteractive(JNIEnv *env, jclass clazz, jboolean enable) {
+    if (gPowerModule) {
+        if (enable) {
             ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(true) while turning screen on");
             gPowerModule->setInteractive(gPowerModule, true);
-        }
-
-        const sp<IBinder>& display = s->getBuiltInDisplay(0);   // TODO: support multiple displays
-        {
-            ALOGD_IF_SLOW(100, "Excessive delay in unblank() while turning screen on");
-            s->unblank(display);
-        }
-    } else {
-        const sp<IBinder>& display = s->getBuiltInDisplay(0);   // TODO: support multiple displays
-        {
-            ALOGD_IF_SLOW(100, "Excessive delay in blank() while turning screen off");
-            s->blank(display);
-        }
-
-        if (gPowerModule) {
+        } else {
             ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(false) while turning screen off");
             gPowerModule->setInteractive(gPowerModule, false);
         }
+    }
+}
 
-        {
-            ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_enable() while turning screen off");
-            autosuspend_enable();
-        }
+static void nativeSetAutoSuspend(JNIEnv *env, jclass clazz, jboolean enable) {
+    if (enable) {
+        ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_enable() while turning screen off");
+        autosuspend_enable();
+    } else {
+        ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_disable() while turning screen on");
+        autosuspend_disable();
     }
 }
 
@@ -235,8 +217,10 @@
             (void*) nativeAcquireSuspendBlocker },
     { "nativeReleaseSuspendBlocker", "(Ljava/lang/String;)V",
             (void*) nativeReleaseSuspendBlocker },
-    { "nativeSetScreenState", "(Z)V",
-            (void*) nativeSetScreenState },
+    { "nativeSetInteractive", "(Z)V",
+            (void*) nativeSetInteractive },
+    { "nativeSetAutoSuspend", "(Z)V",
+            (void*) nativeSetAutoSuspend },
     { "nativeShutdown", "()V",
             (void*) nativeShutdown },
     { "nativeReboot", "(Ljava/lang/String;)V",
diff --git a/wifi/java/android/net/wifi/WifiSsid.java b/wifi/java/android/net/wifi/WifiSsid.java
index 6f36111..3e5f10f 100644
--- a/wifi/java/android/net/wifi/WifiSsid.java
+++ b/wifi/java/android/net/wifi/WifiSsid.java
@@ -156,7 +156,11 @@
 
     @Override
     public String toString() {
-        if (octets.size() <= 0) return "";
+        byte[] ssidBytes = octets.toByteArray();
+        // Supplicant returns \x00\x00\x00\x00\x00\x00\x00\x00 hex string
+        // for a hidden access point. Make sure we maintain the previous
+        // behavior of returning empty string for this case.
+        if (octets.size() <= 0 || isArrayAllZeroes(ssidBytes)) return "";
         // TODO: Handle conversion to other charsets upon failure
         Charset charset = Charset.forName("UTF-8");
         CharsetDecoder decoder = charset.newDecoder()
@@ -164,7 +168,7 @@
                 .onUnmappableCharacter(CodingErrorAction.REPLACE);
         CharBuffer out = CharBuffer.allocate(32);
 
-        CoderResult result = decoder.decode(ByteBuffer.wrap(octets.toByteArray()), out, true);
+        CoderResult result = decoder.decode(ByteBuffer.wrap(ssidBytes), out, true);
         out.flip();
         if (result.isError()) {
             return NONE;
@@ -172,6 +176,13 @@
         return out.toString();
     }
 
+    private boolean isArrayAllZeroes(byte[] ssidBytes) {
+        for (int i = 0; i< ssidBytes.length; i++) {
+            if (ssidBytes[i] != 0) return false;
+        }
+        return true;
+    }
+
     /** @hide */
     public byte[] getOctets() {
         return  octets.toByteArray();
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index 4440145..9c727f9 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -98,6 +98,8 @@
     static final int POOR_LINK_DETECTED                             = BASE + 21;
     static final int GOOD_LINK_DETECTED                             = BASE + 22;
 
+    public static final boolean DEFAULT_POOR_NETWORK_AVOIDANCE_ENABLED = false;
+
     /*
      * RSSI levels as used by notification icon
      * Level 4  -55 <= RSSI
@@ -345,13 +347,6 @@
         // watchdog in an enabled state
         putSettingsGlobalBoolean(contentResolver, Settings.Global.WIFI_WATCHDOG_ON, true);
 
-        // disable poor network avoidance
-        if (sWifiOnly) {
-            logd("Disabling poor network avoidance for wi-fi only device");
-            putSettingsGlobalBoolean(contentResolver,
-                    Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, false);
-        }
-
         WifiWatchdogStateMachine wwsm = new WifiWatchdogStateMachine(context);
         wwsm.start();
         return wwsm;
@@ -441,8 +436,15 @@
     private void updateSettings() {
         if (DBG) logd("Updating secure settings");
 
-        mPoorNetworkDetectionEnabled = getSettingsGlobalBoolean(mContentResolver,
-                Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, true);
+        // disable poor network avoidance
+        if (sWifiOnly) {
+            logd("Disabling poor network avoidance for wi-fi only device");
+            mPoorNetworkDetectionEnabled = false;
+        } else {
+            mPoorNetworkDetectionEnabled = getSettingsGlobalBoolean(mContentResolver,
+                    Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED,
+                    DEFAULT_POOR_NETWORK_AVOIDANCE_ENABLED);
+        }
     }
 
     /**