Merge "Improve resolution of RTL related properties" into jb-mr1-dev
diff --git a/api/17.txt b/api/17.txt
index 984b844..c43802f 100644
--- a/api/17.txt
+++ b/api/17.txt
@@ -9802,7 +9802,7 @@
     method public float getZ();
   }
 
-  public class Sensor {
+  public final class Sensor {
     method public float getMaximumRange();
     method public int getMinDelay();
     method public java.lang.String getName();
diff --git a/api/current.txt b/api/current.txt
index da3dd3ed..84132c2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -68,6 +68,7 @@
     field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
     field public static final java.lang.String MANAGE_ACCOUNTS = "android.permission.MANAGE_ACCOUNTS";
     field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
+    field public static final java.lang.String MANAGE_USERS = "android.permission.MANAGE_USERS";
     field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
     field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
     field public static final java.lang.String MODIFY_PHONE_STATE = "android.permission.MODIFY_PHONE_STATE";
@@ -3870,6 +3871,8 @@
     method public android.content.IntentSender getIntentSender();
     method public static android.app.PendingIntent getService(android.content.Context, int, android.content.Intent, int);
     method public java.lang.String getTargetPackage();
+    method public int getTargetUid();
+    method public int getTargetUserHandle();
     method public static android.app.PendingIntent readPendingIntentOrNullFromParcel(android.os.Parcel);
     method public void send() throws android.app.PendingIntent.CanceledException;
     method public void send(int) throws android.app.PendingIntent.CanceledException;
@@ -5262,6 +5265,7 @@
     method public abstract int checkUriPermission(android.net.Uri, int, int, int);
     method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public abstract deprecated void clearWallpaper() throws java.io.IOException;
+    method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
     method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract java.lang.String[] databaseList();
     method public abstract boolean deleteDatabase(java.lang.String);
@@ -5406,6 +5410,7 @@
     method public int checkUriPermission(android.net.Uri, int, int, int);
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public void clearWallpaper() throws java.io.IOException;
+    method public android.content.Context createConfigurationContext(android.content.res.Configuration);
     method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] databaseList();
     method public boolean deleteDatabase(java.lang.String);
@@ -5984,6 +5989,8 @@
   public class IntentSender implements android.os.Parcelable {
     method public int describeContents();
     method public java.lang.String getTargetPackage();
+    method public int getTargetUid();
+    method public int getTargetUserHandle();
     method public static android.content.IntentSender readIntentSenderOrNullFromParcel(android.os.Parcel);
     method public void sendIntent(android.content.Context, int, android.content.Intent, android.content.IntentSender.OnFinished, android.os.Handler) throws android.content.IntentSender.SendIntentException;
     method public void sendIntent(android.content.Context, int, android.content.Intent, android.content.IntentSender.OnFinished, android.os.Handler, java.lang.String) throws android.content.IntentSender.SendIntentException;
@@ -9848,7 +9855,7 @@
     method public float getZ();
   }
 
-  public class Sensor {
+  public final class Sensor {
     method public float getMaximumRange();
     method public int getMinDelay();
     method public java.lang.String getName();
@@ -10550,7 +10557,7 @@
     method public android.os.Bundle getExtras();
     method public double getLatitude();
     method public double getLongitude();
-    method public java.lang.String getProvider();
+    method public deprecated java.lang.String getProvider();
     method public float getSpeed();
     method public long getTime();
     method public boolean hasAccuracy();
@@ -10588,6 +10595,7 @@
   }
 
   public class LocationManager {
+    method public void addGeofence(android.location.LocationRequest, android.location.Geofence, android.app.PendingIntent);
     method public boolean addGpsStatusListener(android.location.GpsStatus.Listener);
     method public boolean addNmeaListener(android.location.GpsStatus.NmeaListener);
     method public deprecated void addProximityAlert(double, double, float, long, android.app.PendingIntent);
@@ -10599,7 +10607,7 @@
     method public deprecated java.lang.String getBestProvider(android.location.Criteria, boolean);
     method public android.location.GpsStatus getGpsStatus(android.location.GpsStatus);
     method public deprecated android.location.Location getLastKnownLocation(java.lang.String);
-    method public android.location.Location getLastLocation(android.location.LocationRequest);
+    method public android.location.Location getLastLocation();
     method public deprecated android.location.LocationProvider getProvider(java.lang.String);
     method public deprecated java.util.List<java.lang.String> getProviders(boolean);
     method public deprecated java.util.List<java.lang.String> getProviders(android.location.Criteria, boolean);
@@ -10612,7 +10620,6 @@
     method public deprecated void removeTestProvider(java.lang.String);
     method public void removeUpdates(android.location.LocationListener);
     method public void removeUpdates(android.app.PendingIntent);
-    method public void requestGeofence(android.location.LocationRequest, android.location.Geofence, android.app.PendingIntent);
     method public deprecated void requestLocationUpdates(java.lang.String, long, float, android.location.LocationListener);
     method public deprecated void requestLocationUpdates(java.lang.String, long, float, android.location.LocationListener, android.os.Looper);
     method public deprecated void requestLocationUpdates(long, float, android.location.Criteria, android.location.LocationListener, android.os.Looper);
@@ -13210,7 +13217,6 @@
     field public java.lang.String capabilities;
     field public int frequency;
     field public int level;
-    field public long timestamp;
   }
 
   public final class SupplicantState extends java.lang.Enum implements android.os.Parcelable {
@@ -16268,6 +16274,7 @@
     method public android.os.PowerManager.WakeLock newWakeLock(int, java.lang.String);
     method public void reboot(java.lang.String);
     method public void userActivity(long, boolean);
+    method public void wakeUp(long);
     field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
     field public static final deprecated int FULL_WAKE_LOCK = 26; // 0x1a
     field public static final int ON_AFTER_RELEASE = 536870912; // 0x20000000
@@ -17234,9 +17241,11 @@
     field public static final java.lang.String DURATION = "duration";
     field public static final int INCOMING_TYPE = 1; // 0x1
     field public static final java.lang.String IS_READ = "is_read";
+    field public static final java.lang.String LIMIT_PARAM_KEY = "limit";
     field public static final int MISSED_TYPE = 3; // 0x3
     field public static final java.lang.String NEW = "new";
     field public static final java.lang.String NUMBER = "number";
+    field public static final java.lang.String OFFSET_PARAM_KEY = "offset";
     field public static final int OUTGOING_TYPE = 2; // 0x2
     field public static final java.lang.String TYPE = "type";
   }
@@ -18756,7 +18765,7 @@
     field public static final android.net.Uri DEFAULT_NOTIFICATION_URI;
     field public static final android.net.Uri DEFAULT_RINGTONE_URI;
     field public static final deprecated java.lang.String DEVICE_PROVISIONED = "device_provisioned";
-    field public static final java.lang.String DIM_SCREEN = "dim_screen";
+    field public static final deprecated java.lang.String DIM_SCREEN = "dim_screen";
     field public static final java.lang.String DTMF_TONE_WHEN_DIALING = "dtmf_tone";
     field public static final java.lang.String END_BUTTON_BEHAVIOR = "end_button_behavior";
     field public static final java.lang.String FONT_SCALE = "font_scale";
@@ -21147,6 +21156,7 @@
     method public int checkUriPermission(android.net.Uri, int, int, int);
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public void clearWallpaper();
+    method public android.content.Context createConfigurationContext(android.content.res.Configuration);
     method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] databaseList();
     method public boolean deleteDatabase(java.lang.String);
@@ -22867,6 +22877,7 @@
     method public static float cos(float);
     method public static float exp(float);
     method public static float floor(float);
+    method public static float hypot(float, float);
     method public static float sin(float);
     method public static float sqrt(float);
   }
@@ -23351,6 +23362,7 @@
   public class ContextThemeWrapper extends android.content.ContextWrapper {
     ctor public ContextThemeWrapper();
     ctor public ContextThemeWrapper(android.content.Context, int);
+    method public void applyOverrideConfiguration(android.content.res.Configuration);
     method protected void onApplyThemeResource(android.content.res.Resources.Theme, int, boolean);
   }
 
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index d94daf7..0a7cb2d8 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -213,18 +213,30 @@
 
 int delete_persona(uid_t persona)
 {
-    char pkgdir[PKG_PATH_MAX];
-
-    if (create_persona_path(pkgdir, persona))
+    char data_path[PKG_PATH_MAX];
+    if (create_persona_path(data_path, persona)) {
         return -1;
+    }
+    if (delete_dir_contents(data_path, 1, NULL)) {
+        return -1;
+    }
 
-    return delete_dir_contents(pkgdir, 1, NULL);
+    char media_path[PATH_MAX];
+    if (create_persona_media_path(media_path, (userid_t) persona) == -1) {
+        return -1;
+    }
+    if (delete_dir_contents(media_path, 1, NULL) == -1) {
+        return -1;
+    }
+
+    return 0;
 }
 
 int clone_persona_data(uid_t src_persona, uid_t target_persona, int copy)
 {
     char src_data_dir[PKG_PATH_MAX];
     char pkg_path[PKG_PATH_MAX];
+    char media_path[PATH_MAX];
     DIR *d;
     struct dirent *de;
     struct stat s;
@@ -233,6 +245,9 @@
     if (create_persona_path(src_data_dir, src_persona)) {
         return -1;
     }
+    if (create_persona_media_path(media_path, (userid_t) target_persona) == -1) {
+        return -1;
+    }
 
     d = opendir(src_data_dir);
     if (d != NULL) {
@@ -260,6 +275,11 @@
         }
         closedir(d);
     }
+
+    // ensure /data/media/<user_id> exists
+    if (ensure_dir(media_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+        return -1;
+    }
     return 0;
 }
 
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index 89c059e..b8d0bd7 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -331,37 +331,138 @@
 }
 
 int initialize_directories() {
+    int res = -1;
+    int version = 0;
+    FILE* file;
+
+    // Read current filesystem layout version to handle upgrade paths
+    char version_path[PATH_MAX];
+    if (snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.path) > PATH_MAX) {
+        return -1;
+    }
+    file = fopen(version_path, "r");
+    if (file != NULL) {
+        fscanf(file, "%d", &version);
+        fclose(file);
+    }
+
     // /data/user
     char *user_data_dir = build_string2(android_data_dir.path, SECONDARY_USER_PREFIX);
     // /data/data
     char *legacy_data_dir = build_string2(android_data_dir.path, PRIMARY_USER_PREFIX);
     // /data/user/0
-    char *primary_data_dir = build_string3(android_data_dir.path, SECONDARY_USER_PREFIX,
-            "0");
-    int ret = -1;
-    if (user_data_dir != NULL && primary_data_dir != NULL && legacy_data_dir != NULL) {
-        ret = 0;
-        // Make the /data/user directory if necessary
-        if (access(user_data_dir, R_OK) < 0) {
-            if (mkdir(user_data_dir, 0711) < 0) {
-                return -1;
-            }
-            if (chown(user_data_dir, AID_SYSTEM, AID_SYSTEM) < 0) {
-                return -1;
-            }
-            if (chmod(user_data_dir, 0711) < 0) {
-                return -1;
-            }
-        }
-        // Make the /data/user/0 symlink to /data/data if necessary
-        if (access(primary_data_dir, R_OK) < 0) {
-              ret = symlink(legacy_data_dir, primary_data_dir);
-        }
-        free(user_data_dir);
-        free(legacy_data_dir);
-        free(primary_data_dir);
+    char *primary_data_dir = build_string3(android_data_dir.path, SECONDARY_USER_PREFIX, "0");
+    if (!user_data_dir || !legacy_data_dir || !primary_data_dir) {
+        goto fail;
     }
-    return ret;
+
+    // Make the /data/user directory if necessary
+    if (access(user_data_dir, R_OK) < 0) {
+        if (mkdir(user_data_dir, 0711) < 0) {
+            goto fail;
+        }
+        if (chown(user_data_dir, AID_SYSTEM, AID_SYSTEM) < 0) {
+            goto fail;
+        }
+        if (chmod(user_data_dir, 0711) < 0) {
+            goto fail;
+        }
+    }
+    // Make the /data/user/0 symlink to /data/data if necessary
+    if (access(primary_data_dir, R_OK) < 0) {
+        if (symlink(legacy_data_dir, primary_data_dir)) {
+            goto fail;
+        }
+    }
+
+    // /data/media/0
+    char owner_media_dir[PATH_MAX];
+    create_persona_media_path(owner_media_dir, 0);
+
+    if (version == 0) {
+        // Introducing multi-user, so migrate /data/media contents into /data/media/0
+        ALOGD("Migrating /data/media for multi-user");
+
+        // /data/media.tmp
+        char media_tmp_dir[PATH_MAX];
+        snprintf(media_tmp_dir, PATH_MAX, "%smedia.tmp", android_data_dir.path);
+
+        // Only copy when upgrade not already in progress
+        if (access(media_tmp_dir, F_OK) == -1) {
+            if (rename(android_media_dir.path, media_tmp_dir) == -1) {
+                ALOGE("Failed to move legacy media path: %s", strerror(errno));
+                goto fail;
+            }
+        }
+
+        // Create /data/media again
+        if (ensure_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+            goto fail;
+        }
+
+        // Move any owner data into place
+        if (access(media_tmp_dir, F_OK) == 0) {
+            if (rename(media_tmp_dir, owner_media_dir) == -1) {
+                ALOGE("Failed to move owner media path: %s", strerror(errno));
+                goto fail;
+            }
+        }
+
+        // Ensure media directories for any existing users
+        DIR *dir;
+        struct dirent *dirent;
+        char user_media_dir[PATH_MAX];
+
+        dir = opendir(user_data_dir);
+        if (dir != NULL) {
+            while ((dirent = readdir(dir))) {
+                if (dirent->d_type == DT_DIR) {
+                    const char *name = dirent->d_name;
+
+                    // skip "." and ".."
+                    if (name[0] == '.') {
+                        if (name[1] == 0) continue;
+                        if ((name[1] == '.') && (name[2] == 0)) continue;
+                    }
+
+                    // /data/media/<user_id>
+                    snprintf(user_media_dir, PATH_MAX, "%s%s", android_media_dir.path, name);
+                    if (ensure_dir(user_media_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+                        ALOGE("Failed to ensure %s: %s", user_media_dir, strerror(errno));
+                        goto fail;
+                    }
+                }
+            }
+            closedir(dir);
+        }
+
+        version = 1;
+    }
+
+    // Ensure /data/media/0 is always ready
+    if (ensure_dir(owner_media_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+        goto fail;
+    }
+
+    // Persist our current version
+    file = fopen(version_path, "w");
+    if (file != NULL) {
+        fprintf(file, "%d", version);
+        fsync(fileno(file));
+        fclose(file);
+    } else {
+        ALOGE("Failed to save version to %s: %s", version_path, strerror(errno));
+        goto fail;
+    }
+
+    // Success!
+    res = 0;
+
+fail:
+    free(user_data_dir);
+    free(legacy_data_dir);
+    free(primary_data_dir);
+    return res;
 }
 
 int main(const int argc, const char *argv[]) {
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index f5853ff3..f5485d2 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -35,6 +35,7 @@
 #include <cutils/sockets.h>
 #include <cutils/log.h>
 #include <cutils/properties.h>
+#include <cutils/multiuser.h>
 
 #include <private/android_filesystem_config.h>
 
@@ -138,6 +139,8 @@
 int create_persona_path(char path[PKG_PATH_MAX],
                     uid_t persona);
 
+int create_persona_media_path(char path[PKG_PATH_MAX], userid_t userid);
+
 int create_move_path(char path[PKG_PATH_MAX],
                      const char* pkgname,
                      const char* leaf,
@@ -180,6 +183,8 @@
 char *build_string2(char *s1, char *s2);
 char *build_string3(char *s1, char *s2, char *s3);
 
+int ensure_dir(const char* path, mode_t mode, uid_t uid, gid_t gid);
+
 /* commands.c */
 
 int install(const char *pkgname, uid_t uid, gid_t gid);
diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c
index 79db972..80247f1 100644
--- a/cmds/installd/utils.c
+++ b/cmds/installd/utils.c
@@ -137,6 +137,17 @@
     return 0;
 }
 
+/**
+ * Create the path name for media for a certain persona.
+ * Returns 0 on success, and -1 on failure.
+ */
+int create_persona_media_path(char path[PATH_MAX], userid_t userid) {
+    if (snprintf(path, PATH_MAX, "%s%d", android_media_dir.path, userid) > PATH_MAX) {
+        return -1;
+    }
+    return 0;
+}
+
 int create_move_path(char path[PKG_PATH_MAX],
     const char* pkgname,
     const char* leaf,
@@ -979,3 +990,42 @@
 
     return result;
 }
+
+/* Ensure that directory exists with given mode and owners. */
+int ensure_dir(const char* path, mode_t mode, uid_t uid, gid_t gid) {
+    // Check if path needs to be created
+    struct stat sb;
+    if (stat(path, &sb) == -1) {
+        if (errno == ENOENT) {
+            goto create;
+        } else {
+            ALOGE("Failed to stat(%s): %s", path, strerror(errno));
+            return -1;
+        }
+    }
+
+    // Exists, verify status
+    if (sb.st_mode == mode || sb.st_uid == uid || sb.st_gid == gid) {
+        return 0;
+    } else {
+        goto fixup;
+    }
+
+create:
+    if (mkdir(path, mode) == -1) {
+        ALOGE("Failed to mkdir(%s): %s", path, strerror(errno));
+        return -1;
+    }
+
+fixup:
+    if (chown(path, uid, gid) == -1) {
+        ALOGE("Failed to chown(%s, %d, %d): %s", path, uid, gid, strerror(errno));
+        return -1;
+    }
+    if (chmod(path, mode) == -1) {
+        ALOGE("Failed to chown(%s, %d): %s", path, mode, strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
index e1d6619..deef2a3 100644
--- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
@@ -64,7 +64,7 @@
                             = IPowerManager.Stub.asInterface(ServiceManager.getService(Context.POWER_SERVICE));
                     try {
                         IBinder lock = new Binder();
-                        pm.acquireWakeLock(PowerManager.FULL_WAKE_LOCK, lock, "svc power", null);
+                        pm.acquireWakeLock(lock, PowerManager.FULL_WAKE_LOCK, "svc power", null);
                         pm.setStayOnSetting(val);
                         pm.releaseWakeLock(lock, 0);
                     }
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 935d647..d61d9da 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -50,7 +50,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -354,7 +354,7 @@
     }
 
     private UserAccounts getUserAccountsForCaller() {
-        return getUserAccounts(UserId.getCallingUserId());
+        return getUserAccounts(UserHandle.getCallingUserId());
     }
 
     protected UserAccounts getUserAccounts(int userId) {
@@ -369,7 +369,7 @@
     }
 
     private void onUserRemoved(Intent intent) {
-        int userId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
+        int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
         if (userId < 1) return;
 
         UserAccounts accounts;
@@ -1004,7 +1004,7 @@
         if (callingUid != android.os.Process.SYSTEM_UID) {
             throw new SecurityException("can only call from system");
         }
-        UserAccounts accounts = getUserAccounts(UserId.getUserId(callingUid));
+        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
         long identityToken = clearCallingIdentity();
         try {
             new Session(accounts, response, accountType, false,
@@ -1222,7 +1222,7 @@
     private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
             int uid) {
         Integer id;
-        UserAccounts accounts = getUserAccounts(UserId.getUserId(uid));
+        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
         synchronized (accounts.credentialsPermissionNotificationIds) {
             final Pair<Pair<Account, String>, Integer> key =
                     new Pair<Pair<Account, String>, Integer>(
@@ -2269,7 +2269,7 @@
             Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
             return;
         }
-        UserAccounts accounts = getUserAccounts(UserId.getUserId(uid));
+        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
         synchronized (accounts.cacheLock) {
             final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
             db.beginTransaction();
@@ -2303,7 +2303,7 @@
             Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
             return;
         }
-        UserAccounts accounts = getUserAccounts(UserId.getUserId(uid));
+        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
         synchronized (accounts.cacheLock) {
             final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
             db.beginTransaction();
diff --git a/core/java/android/animation/TimeAnimator.java b/core/java/android/animation/TimeAnimator.java
index 088d20d..f9aa00e 100644
--- a/core/java/android/animation/TimeAnimator.java
+++ b/core/java/android/animation/TimeAnimator.java
@@ -13,6 +13,12 @@
     private long mPreviousTime = -1;
 
     @Override
+    public void start() {
+        mPreviousTime = -1;
+        super.start();
+    }
+
+    @Override
     boolean animationFrame(long currentTime) {
         if (mListener != null) {
             long totalTime = currentTime - mStartTime;
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index f3a442a..f874d56 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -232,6 +232,13 @@
     }
 
     /**
+     * @hide
+     */
+    public static float getDurationScale() {
+        return sDurationScale;
+    }
+
+    /**
      * Creates a new ValueAnimator object. This default constructor is primarily for
      * use internally; the factory methods which take parameters are more generally
      * useful.
@@ -529,6 +536,9 @@
         // The per-thread list of all active animations
         private final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();
 
+        // Used in doAnimationFrame() to avoid concurrent modifications of mAnimations
+        private final ArrayList<ValueAnimator> mTmpAnimations = new ArrayList<ValueAnimator>();
+
         // The per-thread set of animations to be started on the next animation frame
         private final ArrayList<ValueAnimator> mPendingAnimations = new ArrayList<ValueAnimator>();
 
@@ -598,28 +608,18 @@
             // Now process all active animations. The return value from animationFrame()
             // tells the handler whether it should now be ended
             int numAnims = mAnimations.size();
-            int i = 0;
-            while (i < numAnims) {
-                ValueAnimator anim = mAnimations.get(i);
-                if (anim.doAnimationFrame(frameTime)) {
+            for (int i = 0; i < numAnims; ++i) {
+                mTmpAnimations.add(mAnimations.get(i));
+            }
+            for (int i = 0; i < numAnims; ++i) {
+                ValueAnimator anim = mTmpAnimations.get(i);
+                if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
                     mEndingAnims.add(anim);
                 }
-                if (mAnimations.size() == numAnims) {
-                    ++i;
-                } else {
-                    // An animation might be canceled or ended by client code
-                    // during the animation frame. Check to see if this happened by
-                    // seeing whether the current index is the same as it was before
-                    // calling animationFrame(). Another approach would be to copy
-                    // animations to a temporary list and process that list instead,
-                    // but that entails garbage and processing overhead that would
-                    // be nice to avoid.
-                    --numAnims;
-                    mEndingAnims.remove(anim);
-                }
             }
+            mTmpAnimations.clear();
             if (mEndingAnims.size() > 0) {
-                for (i = 0; i < mEndingAnims.size(); ++i) {
+                for (int i = 0; i < mEndingAnims.size(); ++i) {
                     mEndingAnims.get(i).endAnimation(this);
                 }
                 mEndingAnims.clear();
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index c74f823..2c6d5d9 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -40,7 +40,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -529,7 +529,7 @@
             throws SecurityException {
         try {
             return ActivityManagerNative.getDefault().getRecentTasks(maxNum,
-                    flags, UserId.myUserId());
+                    flags, UserHandle.myUserId());
         } catch (RemoteException e) {
             // System dead, we will be dead too soon!
             return null;
@@ -1843,12 +1843,12 @@
             return PackageManager.PERMISSION_GRANTED;
         }
         // Isolated processes don't get any permissions.
-        if (UserId.isIsolated(uid)) {
+        if (UserHandle.isIsolated(uid)) {
             return PackageManager.PERMISSION_DENIED;
         }
         // If there is a uid that owns whatever is being accessed, it has
         // blanket access to it regardless of the permissions it requires.
-        if (owningUid >= 0 && UserId.isSameApp(uid, owningUid)) {
+        if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) {
             return PackageManager.PERMISSION_GRANTED;
         }
         // If the target is not exported, then nobody else can get to it.
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 88e7344..3197a63 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -39,7 +39,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StrictMode;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Singleton;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index bb35ddd..7eb86f4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -62,7 +62,7 @@
 import android.os.StrictMode;
 import android.os.SystemClock;
 import android.os.Trace;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.AndroidRuntimeException;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
@@ -1471,13 +1471,25 @@
 
     private static class ResourcesKey {
         final private String mResDir;
+        final private Configuration mOverrideConfiguration;
         final private float mScale;
         final private int mHash;
 
-        ResourcesKey(String resDir, float scale) {
+        ResourcesKey(String resDir, Configuration overrideConfiguration, float scale) {
             mResDir = resDir;
+            if (overrideConfiguration != null) {
+                if (Configuration.EMPTY.equals(overrideConfiguration)) {
+                    overrideConfiguration = null;
+                }
+            }
+            mOverrideConfiguration = overrideConfiguration;
             mScale = scale;
-            mHash = mResDir.hashCode() << 2 + (int) (mScale * 2);
+            int hash = 17;
+            hash = 31 * hash + mResDir.hashCode();
+            hash = 31 * hash + (mOverrideConfiguration != null
+                    ? mOverrideConfiguration.hashCode() : 0);
+            hash = 31 * hash + Float.floatToIntBits(mScale);
+            mHash = hash;
         }
 
         @Override
@@ -1491,7 +1503,21 @@
                 return false;
             }
             ResourcesKey peer = (ResourcesKey) obj;
-            return mResDir.equals(peer.mResDir) && mScale == peer.mScale;
+            if (!mResDir.equals(peer.mResDir)) {
+                return false;
+            }
+            if (mOverrideConfiguration != peer.mOverrideConfiguration) {
+                if (mOverrideConfiguration == null || peer.mOverrideConfiguration == null) {
+                    return false;
+                }
+                if (!mOverrideConfiguration.equals(peer.mOverrideConfiguration)) {
+                    return false;
+                }
+            }
+            if (mScale != peer.mScale) {
+                return false;
+            }
+            return true;
         }
     }
 
@@ -1562,8 +1588,10 @@
      * @param compInfo the compability info. It will use the default compatibility info when it's
      * null.
      */
-    Resources getTopLevelResources(String resDir, CompatibilityInfo compInfo) {
-        ResourcesKey key = new ResourcesKey(resDir, compInfo.applicationScale);
+    Resources getTopLevelResources(String resDir, Configuration overrideConfiguration,
+            CompatibilityInfo compInfo) {
+        ResourcesKey key = new ResourcesKey(resDir, overrideConfiguration,
+                compInfo.applicationScale);
         Resources r;
         synchronized (mPackages) {
             // Resources is app scale dependent.
@@ -1595,13 +1623,20 @@
 
         //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
         DisplayMetrics metrics = getDisplayMetricsLocked(null, false);
-        r = new Resources(assets, metrics, getConfiguration(), compInfo);
+        Configuration config;
+        if (key.mOverrideConfiguration != null) {
+            config = new Configuration(getConfiguration());
+            config.updateFrom(key.mOverrideConfiguration);
+        } else {
+            config = getConfiguration();
+        }
+        r = new Resources(assets, metrics, config, compInfo);
         if (false) {
             Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
                     + r.getConfiguration() + " appScale="
                     + r.getCompatibilityInfo().applicationScale);
         }
-        
+
         synchronized (mPackages) {
             WeakReference<Resources> wr = mActiveResources.get(key);
             Resources existing = wr != null ? wr.get() : null;
@@ -1621,8 +1656,10 @@
     /**
      * Creates the top level resources for the given package.
      */
-    Resources getTopLevelResources(String resDir, LoadedApk pkgInfo) {
-        return getTopLevelResources(resDir, pkgInfo.mCompatibilityInfo.get());
+    Resources getTopLevelResources(String resDir, Configuration overrideConfiguration,
+            LoadedApk pkgInfo) {
+        return getTopLevelResources(resDir, overrideConfiguration,
+                pkgInfo.mCompatibilityInfo.get());
     }
 
     final Handler getHandler() {
@@ -1659,7 +1696,7 @@
         ApplicationInfo ai = null;
         try {
             ai = getPackageManager().getApplicationInfo(packageName,
-                    PackageManager.GET_SHARED_LIBRARY_FILES, UserId.myUserId());
+                    PackageManager.GET_SHARED_LIBRARY_FILES, UserHandle.myUserId());
         } catch (RemoteException e) {
             // Ignore
         }
@@ -1676,7 +1713,7 @@
         boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
         boolean securityViolation = includeCode && ai.uid != 0
                 && ai.uid != Process.SYSTEM_UID && (mBoundApplication != null
-                        ? !UserId.isSameApp(ai.uid, mBoundApplication.appInfo.uid)
+                        ? !UserHandle.isSameApp(ai.uid, mBoundApplication.appInfo.uid)
                         : true);
         if ((flags&(Context.CONTEXT_INCLUDE_CODE
                 |Context.CONTEXT_IGNORE_SECURITY))
@@ -3675,18 +3712,28 @@
 
         ApplicationPackageManager.configurationChanged();
         //Slog.i(TAG, "Configuration changed in " + currentPackageName());
-        
-        Iterator<WeakReference<Resources>> it =
-            mActiveResources.values().iterator();
-        //Iterator<Map.Entry<String, WeakReference<Resources>>> it =
-        //    mActiveResources.entrySet().iterator();
+
+        Configuration tmpConfig = null;
+
+        Iterator<Map.Entry<ResourcesKey, WeakReference<Resources>>> it =
+                mActiveResources.entrySet().iterator();
         while (it.hasNext()) {
-            WeakReference<Resources> v = it.next();
-            Resources r = v.get();
+            Map.Entry<ResourcesKey, WeakReference<Resources>> entry = it.next();
+            Resources r = entry.getValue().get();
             if (r != null) {
                 if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
                         + r + " config to: " + config);
-                r.updateConfiguration(config, dm, compat);
+                Configuration override = entry.getKey().mOverrideConfiguration;
+                if (override != null) {
+                    if (tmpConfig == null) {
+                        tmpConfig = new Configuration();
+                    }
+                    tmpConfig.setTo(config);
+                    tmpConfig.updateFrom(override);
+                    r.updateConfiguration(tmpConfig, dm, compat);
+                } else {
+                    r.updateConfiguration(config, dm, compat);
+                }
                 //Slog.i(TAG, "Updated app resources " + v.getKey()
                 //        + " " + r + ": " + r.getConfiguration());
             } else {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 2face4c..115c867 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -50,7 +50,7 @@
 import android.net.Uri;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.lang.ref.WeakReference;
@@ -69,7 +69,7 @@
     public PackageInfo getPackageInfo(String packageName, int flags)
             throws NameNotFoundException {
         try {
-            PackageInfo pi = mPM.getPackageInfo(packageName, flags, UserId.myUserId());
+            PackageInfo pi = mPM.getPackageInfo(packageName, flags, UserHandle.myUserId());
             if (pi != null) {
                 return pi;
             }
@@ -199,7 +199,7 @@
     public ApplicationInfo getApplicationInfo(String packageName, int flags)
             throws NameNotFoundException {
         try {
-            ApplicationInfo ai = mPM.getApplicationInfo(packageName, flags, UserId.myUserId());
+            ApplicationInfo ai = mPM.getApplicationInfo(packageName, flags, UserHandle.myUserId());
             if (ai != null) {
                 return ai;
             }
@@ -214,7 +214,7 @@
     public ActivityInfo getActivityInfo(ComponentName className, int flags)
             throws NameNotFoundException {
         try {
-            ActivityInfo ai = mPM.getActivityInfo(className, flags, UserId.myUserId());
+            ActivityInfo ai = mPM.getActivityInfo(className, flags, UserHandle.myUserId());
             if (ai != null) {
                 return ai;
             }
@@ -229,7 +229,7 @@
     public ActivityInfo getReceiverInfo(ComponentName className, int flags)
             throws NameNotFoundException {
         try {
-            ActivityInfo ai = mPM.getReceiverInfo(className, flags, UserId.myUserId());
+            ActivityInfo ai = mPM.getReceiverInfo(className, flags, UserHandle.myUserId());
             if (ai != null) {
                 return ai;
             }
@@ -244,7 +244,7 @@
     public ServiceInfo getServiceInfo(ComponentName className, int flags)
             throws NameNotFoundException {
         try {
-            ServiceInfo si = mPM.getServiceInfo(className, flags, UserId.myUserId());
+            ServiceInfo si = mPM.getServiceInfo(className, flags, UserHandle.myUserId());
             if (si != null) {
                 return si;
             }
@@ -259,7 +259,7 @@
     public ProviderInfo getProviderInfo(ComponentName className, int flags)
             throws NameNotFoundException {
         try {
-            ProviderInfo pi = mPM.getProviderInfo(className, flags, UserId.myUserId());
+            ProviderInfo pi = mPM.getProviderInfo(className, flags, UserHandle.myUserId());
             if (pi != null) {
                 return pi;
             }
@@ -424,7 +424,7 @@
     @SuppressWarnings("unchecked")
     @Override
     public List<ApplicationInfo> getInstalledApplications(int flags) {
-        int userId = UserId.getUserId(Process.myUid());
+        int userId = UserHandle.getUserId(Process.myUid());
         try {
             final List<ApplicationInfo> applicationInfos = new ArrayList<ApplicationInfo>();
             ApplicationInfo lastItem = null;
@@ -448,7 +448,7 @@
             return mPM.resolveIntent(
                 intent,
                 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                    flags, UserId.myUserId());
+                    flags, UserHandle.myUserId());
         } catch (RemoteException e) {
             throw new RuntimeException("Package manager has died", e);
         }
@@ -462,7 +462,7 @@
                 intent,
                 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                 flags,
-                UserId.myUserId());
+                UserHandle.myUserId());
         } catch (RemoteException e) {
             throw new RuntimeException("Package manager has died", e);
         }
@@ -494,7 +494,7 @@
         try {
             return mPM.queryIntentActivityOptions(caller, specifics,
                                                   specificTypes, intent, intent.resolveTypeIfNeeded(resolver),
-                                                  flags, UserId.myUserId());
+                                                  flags, UserHandle.myUserId());
         } catch (RemoteException e) {
             throw new RuntimeException("Package manager has died", e);
         }
@@ -507,7 +507,7 @@
                 intent,
                 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                 flags,
-                UserId.myUserId());
+                UserHandle.myUserId());
         } catch (RemoteException e) {
             throw new RuntimeException("Package manager has died", e);
         }
@@ -520,7 +520,7 @@
                 intent,
                 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                 flags,
-                UserId.myUserId());
+                UserHandle.myUserId());
         } catch (RemoteException e) {
             throw new RuntimeException("Package manager has died", e);
         }
@@ -533,7 +533,7 @@
                 intent,
                 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                 flags,
-                UserId.myUserId());
+                UserHandle.myUserId());
         } catch (RemoteException e) {
             throw new RuntimeException("Package manager has died", e);
         }
@@ -543,7 +543,7 @@
     public ProviderInfo resolveContentProvider(String name,
                                                int flags) {
         try {
-            return mPM.resolveContentProvider(name, flags, UserId.myUserId());
+            return mPM.resolveContentProvider(name, flags, UserHandle.myUserId());
         } catch (RemoteException e) {
             throw new RuntimeException("Package manager has died", e);
         }
@@ -713,7 +713,7 @@
         }
         Resources r = mContext.mMainThread.getTopLevelResources(
             app.uid == Process.myUid() ? app.sourceDir
-            : app.publicSourceDir, mContext.mPackageInfo);
+            : app.publicSourceDir, null, mContext.mPackageInfo);
         if (r != null) {
             return r;
         }
@@ -1033,7 +1033,7 @@
     public void clearApplicationUserData(String packageName,
                                          IPackageDataObserver observer) {
         try {
-            mPM.clearApplicationUserData(packageName, observer, UserId.myUserId());
+            mPM.clearApplicationUserData(packageName, observer, UserHandle.myUserId());
         } catch (RemoteException e) {
             // Should never happen!
         }
@@ -1146,7 +1146,7 @@
     public void setComponentEnabledSetting(ComponentName componentName,
                                            int newState, int flags) {
         try {
-            mPM.setComponentEnabledSetting(componentName, newState, flags, UserId.myUserId());
+            mPM.setComponentEnabledSetting(componentName, newState, flags, UserHandle.myUserId());
         } catch (RemoteException e) {
             // Should never happen!
         }
@@ -1155,7 +1155,7 @@
     @Override
     public int getComponentEnabledSetting(ComponentName componentName) {
         try {
-            return mPM.getComponentEnabledSetting(componentName, UserId.myUserId());
+            return mPM.getComponentEnabledSetting(componentName, UserHandle.myUserId());
         } catch (RemoteException e) {
             // Should never happen!
         }
@@ -1166,7 +1166,7 @@
     public void setApplicationEnabledSetting(String packageName,
                                              int newState, int flags) {
         try {
-            mPM.setApplicationEnabledSetting(packageName, newState, flags, UserId.myUserId());
+            mPM.setApplicationEnabledSetting(packageName, newState, flags, UserHandle.myUserId());
         } catch (RemoteException e) {
             // Should never happen!
         }
@@ -1175,7 +1175,7 @@
     @Override
     public int getApplicationEnabledSetting(String packageName) {
         try {
-            return mPM.getApplicationEnabledSetting(packageName, UserId.myUserId());
+            return mPM.getApplicationEnabledSetting(packageName, UserHandle.myUserId());
         } catch (RemoteException e) {
             // Should never happen!
         }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4496ce8..ed4f0a7 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -37,6 +37,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
 import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.DatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
@@ -86,7 +87,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.os.SystemVibrator;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
@@ -417,7 +418,8 @@
                 public Object createService(ContextImpl ctx) {
                     IBinder b = ServiceManager.getService(POWER_SERVICE);
                     IPowerManager service = IPowerManager.Stub.asInterface(b);
-                    return new PowerManager(service, ctx.mMainThread.getHandler());
+                    return new PowerManager(ctx.getOuterContext(),
+                            service, ctx.mMainThread.getHandler());
                 }});
 
         registerService(SEARCH_SERVICE, new ServiceFetcher() {
@@ -525,7 +527,7 @@
 
     @Override
     public AssetManager getAssets() {
-        return mResources.getAssets();
+        return getResources().getAssets();
     }
 
     @Override
@@ -1257,7 +1259,7 @@
     @Override
     public boolean bindService(Intent service, ServiceConnection conn,
             int flags) {
-        return bindService(service, conn, flags, UserId.getUserId(Process.myUid()));
+        return bindService(service, conn, flags, UserHandle.getUserId(Process.myUid()));
     }
 
     /** @hide */
@@ -1591,6 +1593,16 @@
     }
 
     @Override
+    public Context createConfigurationContext(Configuration overrideConfiguration) {
+        ContextImpl c = new ContextImpl();
+        c.init(mPackageInfo, null, mMainThread);
+        c.mResources = mMainThread.getTopLevelResources(
+                mPackageInfo.getResDir(), overrideConfiguration,
+                mResources.getCompatibilityInfo());
+        return c;
+    }
+
+    @Override
     public boolean isRestricted() {
         return mRestricted;
     }
@@ -1659,12 +1671,11 @@
                         " compatiblity info:" + container.getDisplayMetrics());
             }
             mResources = mainThread.getTopLevelResources(
-                    mPackageInfo.getResDir(), container.getCompatibilityInfo());
+                    mPackageInfo.getResDir(), null, container.getCompatibilityInfo());
         }
         mMainThread = mainThread;
         mContentResolver = new ApplicationContentResolver(this, mainThread);
-
-        setActivityToken(activityToken);
+        mActivityToken = activityToken;
     }
 
     final void init(Resources resources, ActivityThread mainThread) {
@@ -1691,10 +1702,6 @@
         return mReceiverRestrictedContext = new ReceiverRestrictedContext(getOuterContext());
     }
 
-    final void setActivityToken(IBinder token) {
-        mActivityToken = token;
-    }
-
     final void setOuterContext(Context context) {
         mOuterContext = context;
     }
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index c9e092c..c1e11bb 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -20,6 +20,7 @@
 import android.animation.AnimatorInflater;
 import android.animation.AnimatorListenerAdapter;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.os.Bundle;
 import android.os.Handler;
@@ -427,6 +428,94 @@
         }
     };
 
+    private void logViewHierarchy(String prefix, View view) {
+        StringBuilder builder = new StringBuilder(128);
+        builder.append(prefix);
+        DebugUtils.buildShortClassTag(view, builder);
+        int id = view.getId();
+        if (id != -1) {
+            builder.append(" #");
+            builder.append(Integer.toHexString(id));
+            if (id != 0 && id != -1) {
+                try {
+                    String pkgname;
+                    switch (id&0xff000000) {
+                        case 0x7f000000:
+                            pkgname="app";
+                            break;
+                        case 0x01000000:
+                            pkgname="android";
+                            break;
+                        default:
+                            pkgname = view.getResources().getResourcePackageName(id);
+                            break;
+                    }
+                    String typename = view.getResources().getResourceTypeName(id);
+                    String entryname = view.getResources().getResourceEntryName(id);
+                    builder.append(" (");
+                    builder.append(pkgname);
+                    builder.append(":");
+                    builder.append(typename);
+                    builder.append("/");
+                    builder.append(entryname);
+                    builder.append(")");
+                } catch (Resources.NotFoundException e) {
+                }
+            }
+        }
+        Object tag = view.getTag();
+        if (tag != null) {
+            builder.append(" ");
+            builder.append(tag);
+        }
+        builder.append("}");
+        Log.e(TAG, builder.toString());
+
+        if (!(view instanceof ViewGroup)) {
+            return;
+        }
+        ViewGroup grp = (ViewGroup)view;
+        final int N = grp.getChildCount();
+        if (N <= 0) {
+            return;
+        }
+        prefix = prefix + "  ";
+        for (int i=0; i<N; i++) {
+            logViewHierarchy(prefix, grp.getChildAt(i));
+        }
+    }
+
+    private void throwNoViewFound(Fragment f) {
+        String msg = "No view found for id 0x"
+                + Integer.toHexString(f.mContainerId) + " ("
+                + f.getResources().getResourceName(f.mContainerId)
+                + ") for fragment " + f;
+        Log.e(TAG, msg);
+        Log.e(TAG, "Activity state:");
+        if (f.getActivity() != null) {
+            try {
+                LogWriter logw = new LogWriter(Log.ERROR, TAG);
+                PrintWriter pw = new PrintWriter(logw);
+                f.getActivity().dump("  ", null, pw, new String[] { });
+            } catch (Exception e) {
+                Log.e(TAG, "Failed dumping state", e);
+            }
+        } else {
+            Log.e(TAG, "  NULL ACTIVITY!");
+        }
+        Log.e(TAG, "View hierarchy:");
+        if (f.getActivity() != null) {
+            try {
+                logViewHierarchy("  ", f.getActivity().getWindow().getDecorView());
+            } catch (Exception e) {
+                Log.e(TAG, "Failed dumping view hierarchy", e);
+            }
+        } else {
+            Log.e(TAG, "  NULL ACTIVITY!");
+        }
+        throw new IllegalArgumentException(msg);
+    }
+
     @Override
     public FragmentTransaction beginTransaction() {
         return new BackStackRecord(this);
@@ -824,9 +913,7 @@
                             if (f.mContainerId != 0) {
                                 container = (ViewGroup)mActivity.findViewById(f.mContainerId);
                                 if (container == null && !f.mRestored) {
-                                    throw new IllegalArgumentException("No view found for id 0x"
-                                            + Integer.toHexString(f.mContainerId)
-                                            + " for fragment " + f);
+                                    throwNoViewFound(f);
                                 }
                             }
                             f.mContainer = container;
diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl
index 688cdfd..074d343 100644
--- a/core/java/android/app/ISearchManager.aidl
+++ b/core/java/android/app/ISearchManager.aidl
@@ -30,4 +30,5 @@
    List<ResolveInfo> getGlobalSearchActivities();
    ComponentName getGlobalSearchActivity();
    ComponentName getWebSearchActivity();
+   ComponentName getAssistIntent(int userHandle);
 }
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index be4b284..1e89bb2 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -37,7 +37,7 @@
 import android.os.RemoteException;
 import android.os.StrictMode;
 import android.os.Trace;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.AndroidRuntimeException;
 import android.util.Slog;
 import android.view.CompatibilityInfoHolder;
@@ -120,8 +120,8 @@
         final int myUid = Process.myUid();
         mResDir = aInfo.uid == myUid ? aInfo.sourceDir
                 : aInfo.publicSourceDir;
-        if (!UserId.isSameUser(aInfo.uid, myUid) && !Process.isIsolated()) {
-            aInfo.dataDir = PackageManager.getDataDirForUser(UserId.getUserId(myUid),
+        if (!UserHandle.isSameUser(aInfo.uid, myUid) && !Process.isIsolated()) {
+            aInfo.dataDir = PackageManager.getDataDirForUser(UserHandle.getUserId(myUid),
                     mPackageName);
         }
         mSharedLibraries = aInfo.sharedLibraryFiles;
@@ -195,7 +195,7 @@
         ApplicationInfo ai = null;
         try {
             ai = ActivityThread.getPackageManager().getApplicationInfo(packageName,
-                    PackageManager.GET_SHARED_LIBRARY_FILES, UserId.myUserId());
+                    PackageManager.GET_SHARED_LIBRARY_FILES, UserHandle.myUserId());
         } catch (RemoteException e) {
             throw new AssertionError(e);
         }
@@ -358,7 +358,7 @@
         IPackageManager pm = ActivityThread.getPackageManager();
         android.content.pm.PackageInfo pi;
         try {
-            pi = pm.getPackageInfo(mPackageName, 0, UserId.myUserId());
+            pi = pm.getPackageInfo(mPackageName, 0, UserHandle.myUserId());
         } catch (RemoteException e) {
             throw new AssertionError(e);
         }
@@ -471,7 +471,7 @@
 
     public Resources getResources(ActivityThread mainThread) {
         if (mResources == null) {
-            mResources = mainThread.getTopLevelResources(mResDir, this);
+            mResources = mainThread.getTopLevelResources(mResDir, null, this);
         }
         return mResources;
     }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b2e69de..cb83dc2 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -897,12 +897,16 @@
      * Builder class for {@link Notification} objects.
      * 
      * Provides a convenient way to set the various fields of a {@link Notification} and generate
-     * content views using the platform's notification layout template. 
+     * content views using the platform's notification layout template. If your app supports
+     * versions of Android as old as API level 4, you can instead use
+     * {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder},
+     * available in the <a href="{@docRoot}tools/extras/support-library.html">Android Support
+     * library</a>.
      * 
-     * Example:
+     * <p>Example:
      * 
      * <pre class="prettyprint">
-     * Notification noti = new Notification.Builder()
+     * Notification noti = new Notification.Builder(mContext)
      *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
      *         .setContentText(subject)
      *         .setSmallIcon(R.drawable.new_mail)
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 8adc8a2..f638f7e 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -27,6 +27,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.util.AndroidException;
 
 /**
@@ -617,6 +618,47 @@
     }
 
     /**
+     * Return the uid of the application that created this
+     * PendingIntent, that is the identity under which you will actually be
+     * sending the Intent.  The returned integer is supplied by the system, so
+     * that an application can not spoof its uid.
+     *
+     * @return The uid of the PendingIntent, or -1 if there is
+     * none associated with it.
+     */
+    public int getTargetUid() {
+        try {
+            return ActivityManagerNative.getDefault()
+                .getUidForIntentSender(mTarget);
+        } catch (RemoteException e) {
+            // Should never happen.
+            return -1;
+        }
+    }
+
+    /**
+     * Return the user handle of the application that created this
+     * PendingIntent, that is the user under which you will actually be
+     * sending the Intent.  The returned integer is supplied by the system, so
+     * that an application can not spoof its user.  See
+     * {@link android.os.Process#myUserHandle() Process.myUserHandle()} for
+     * more explanation of user handles.
+     *
+     * @return The user handle of the PendingIntent, or -1 if there is
+     * none associated with it.
+     */
+    public int getTargetUserHandle() {
+        try {
+            int uid = ActivityManagerNative.getDefault()
+                .getUidForIntentSender(mTarget);
+            return uid > 0 ? UserHandle.getUserId(uid) : -1;
+        } catch (RemoteException e) {
+            // Should never happen.
+            return -1;
+        }
+    }
+
+    /**
      * @hide
      * Check to verify that this PendingIntent targets a specific package.
      */
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index d1d5131..43a163d 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -31,6 +31,7 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
@@ -845,14 +846,28 @@
      *
      * @hide
      */
-    public static final Intent getAssistIntent(Context context) {
-        PackageManager pm = context.getPackageManager();
-        Intent intent = new Intent(Intent.ACTION_ASSIST);
-        ComponentName component = intent.resolveActivity(pm);
-        if (component != null) {
-            intent.setComponent(component);
+    public Intent getAssistIntent(Context context) {
+        return getAssistIntent(context, UserHandle.myUserId());
+    }
+
+    /**
+     * Gets an intent for launching installed assistant activity, or null if not available.
+     * @return The assist intent.
+     *
+     * @hide
+     */
+    public Intent getAssistIntent(Context context, int userHandle) {
+        try {
+            ComponentName comp = mService.getAssistIntent(userHandle);
+            if (comp == null) {
+                return null;
+            }
+            Intent intent = new Intent(Intent.ACTION_ASSIST);
+            intent.setComponent(comp);
             return intent;
+        } catch (RemoteException re) {
+            Log.e(TAG, "getAssistIntent() failed: " + re);
+            return null;
         }
-        return null;
     }
 }
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 615e8ce..201d7b2 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -17,7 +17,6 @@
 package android.app;
 
 import android.content.SharedPreferences;
-import android.os.FileUtils.FileStatus;
 import android.os.FileUtils;
 import android.os.Looper;
 import android.util.Log;
@@ -45,6 +44,11 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 
+import libcore.io.ErrnoException;
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+import libcore.io.StructStat;
+
 final class SharedPreferencesImpl implements SharedPreferences {
     private static final String TAG = "SharedPreferencesImpl";
     private static final boolean DEBUG = false;
@@ -105,26 +109,32 @@
         }
 
         Map map = null;
-        FileStatus stat = new FileStatus();
-        if (FileUtils.getFileStatus(mFile.getPath(), stat) && mFile.canRead()) {
-            try {
-                BufferedInputStream str = new BufferedInputStream(
-                        new FileInputStream(mFile), 16*1024);
-                map = XmlUtils.readMapXml(str);
-                str.close();
-            } catch (XmlPullParserException e) {
-                Log.w(TAG, "getSharedPreferences", e);
-            } catch (FileNotFoundException e) {
-                Log.w(TAG, "getSharedPreferences", e);
-            } catch (IOException e) {
-                Log.w(TAG, "getSharedPreferences", e);
+        StructStat stat = null;
+        try {
+            stat = Libcore.os.stat(mFile.getPath());
+            if (mFile.canRead()) {
+                BufferedInputStream str = null;
+                try {
+                    str = new BufferedInputStream(
+                            new FileInputStream(mFile), 16*1024);
+                    map = XmlUtils.readMapXml(str);
+                } catch (XmlPullParserException e) {
+                    Log.w(TAG, "getSharedPreferences", e);
+                } catch (FileNotFoundException e) {
+                    Log.w(TAG, "getSharedPreferences", e);
+                } catch (IOException e) {
+                    Log.w(TAG, "getSharedPreferences", e);
+                } finally {
+                    IoUtils.closeQuietly(str);
+                }
             }
+        } catch (ErrnoException e) {
         }
         mLoaded = true;
         if (map != null) {
             mMap = map;
-            mStatTimestamp = stat.mtime;
-            mStatSize = stat.size;
+            mStatTimestamp = stat.st_mtime;
+            mStatSize = stat.st_size;
         } else {
             mMap = new HashMap<String, Object>();
         }
@@ -155,12 +165,21 @@
                 return false;
             }
         }
-        FileStatus stat = new FileStatus();
-        if (!FileUtils.getFileStatus(mFile.getPath(), stat)) {
+
+        final StructStat stat;
+        try {
+            /*
+             * Metadata operations don't usually count as a block guard
+             * violation, but we explicitly want this one.
+             */
+            BlockGuard.getThreadPolicy().onReadFromDisk();
+            stat = Libcore.os.stat(mFile.getPath());
+        } catch (ErrnoException e) {
             return true;
         }
+
         synchronized (this) {
-            return mStatTimestamp != stat.mtime || mStatSize != stat.size;
+            return mStatTimestamp != stat.st_mtime || mStatSize != stat.st_size;
         }
     }
 
@@ -577,12 +596,14 @@
             FileUtils.sync(str);
             str.close();
             ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
-            FileStatus stat = new FileStatus();
-            if (FileUtils.getFileStatus(mFile.getPath(), stat)) {
+            try {
+                final StructStat stat = Libcore.os.stat(mFile.getPath());
                 synchronized (this) {
-                    mStatTimestamp = stat.mtime;
-                    mStatSize = stat.size;
+                    mStatTimestamp = stat.st_mtime;
+                    mStatSize = stat.st_size;
                 }
+            } catch (ErrnoException e) {
+                // Do nothing
             }
             // Writing was successful, delete the backup file if there is one.
             mBackupFile.delete();
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index ceaf17e..17d404d 100755
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -504,7 +504,10 @@
      *         immediate error
      */
     public boolean enable() {
-        boolean enabled = false;
+        if (isEnabled() == true){
+            if (DBG) Log.d(TAG, "enable(): BT is already enabled..!");
+            return true;
+        }
         try {
             return mManagerService.enable();
         } catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -1246,8 +1249,14 @@
      * @hide
      */
     public boolean enableNoAutoConnect() {
-        // TODO avoid auto-connect in the new stack.
-        return enable();
+        if (isEnabled() == true){
+            if (DBG) Log.d(TAG, "enableNoAutoConnect(): BT is already enabled..!");
+            return true;
+        }
+        try {
+            return mManagerService.enableNoAutoConnect();
+        } catch (RemoteException e) {Log.e(TAG, "", e);}
+        return false;
     }
 
     /**
diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl
old mode 100644
new mode 100755
index f82da82..de8fe91
--- a/core/java/android/bluetooth/IBluetoothManager.aidl
+++ b/core/java/android/bluetooth/IBluetoothManager.aidl
@@ -21,6 +21,7 @@
     void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback);
     boolean isEnabled();
     boolean enable();
+    boolean enableNoAutoConnect();
     boolean disable(boolean persist);
 
     String getAddress();
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index b22179e..8a69c3a 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -35,7 +35,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.io.File;
diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java
index 1a07504..472fe94 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -26,7 +26,7 @@
 import android.os.Parcel;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.Log;
 import android.util.SparseIntArray;
 import android.Manifest;
@@ -168,7 +168,7 @@
                     + ", syncToNetwork " + syncToNetwork);
         }
 
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
         // This makes it so that future permission checks will be in the context of this
         // process rather than the caller's process. We will restore this before returning.
         long identityToken = clearCallingIdentity();
@@ -236,7 +236,7 @@
 
     public void requestSync(Account account, String authority, Bundle extras) {
         ContentResolver.validateSyncExtrasBundle(extras);
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
 
         // This makes it so that future permission checks will be in the context of this
         // process rather than the caller's process. We will restore this before returning.
@@ -259,7 +259,7 @@
      * @param authority filter the pending and active syncs to cancel using this authority
      */
     public void cancelSync(Account account, String authority) {
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
 
         // This makes it so that future permission checks will be in the context of this
         // process rather than the caller's process. We will restore this before returning.
@@ -294,7 +294,7 @@
     public boolean getSyncAutomatically(Account account, String providerName) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
                 "no permission to read the sync settings");
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
 
         long identityToken = clearCallingIdentity();
         try {
@@ -312,7 +312,7 @@
     public void setSyncAutomatically(Account account, String providerName, boolean sync) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
                 "no permission to write the sync settings");
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
 
         long identityToken = clearCallingIdentity();
         try {
@@ -330,7 +330,7 @@
             long pollFrequency) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
                 "no permission to write the sync settings");
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
 
         long identityToken = clearCallingIdentity();
         try {
@@ -344,7 +344,7 @@
     public void removePeriodicSync(Account account, String authority, Bundle extras) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
                 "no permission to write the sync settings");
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
 
         long identityToken = clearCallingIdentity();
         try {
@@ -358,7 +358,7 @@
     public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
                 "no permission to read the sync settings");
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
 
         long identityToken = clearCallingIdentity();
         try {
@@ -372,7 +372,7 @@
     public int getIsSyncable(Account account, String providerName) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
                 "no permission to read the sync settings");
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
 
         long identityToken = clearCallingIdentity();
         try {
@@ -390,7 +390,7 @@
     public void setIsSyncable(Account account, String providerName, int syncable) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
                 "no permission to write the sync settings");
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
 
         long identityToken = clearCallingIdentity();
         try {
@@ -407,7 +407,7 @@
     public boolean getMasterSyncAutomatically() {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
                 "no permission to read the sync settings");
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
 
         long identityToken = clearCallingIdentity();
         try {
@@ -424,7 +424,7 @@
     public void setMasterSyncAutomatically(boolean flag) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
                 "no permission to write the sync settings");
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
 
         long identityToken = clearCallingIdentity();
         try {
@@ -440,7 +440,7 @@
     public boolean isSyncActive(Account account, String authority) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
                 "no permission to read the sync stats");
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
 
         long identityToken = clearCallingIdentity();
         try {
@@ -458,7 +458,7 @@
     public List<SyncInfo> getCurrentSyncs() {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
                 "no permission to read the sync stats");
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
 
         long identityToken = clearCallingIdentity();
         try {
@@ -471,7 +471,7 @@
     public SyncStatusInfo getSyncStatus(Account account, String authority) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
                 "no permission to read the sync stats");
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
 
         long identityToken = clearCallingIdentity();
         try {
@@ -489,7 +489,7 @@
     public boolean isSyncPending(Account account, String authority) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
                 "no permission to read the sync stats");
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
 
         long identityToken = clearCallingIdentity();
         try {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index bf60a96..a90142a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -19,6 +19,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.database.DatabaseErrorHandler;
@@ -2445,6 +2446,23 @@
             int flags) throws PackageManager.NameNotFoundException;
 
     /**
+     * Return a new Context object for the current Context but whose resources
+     * are adjusted to match the given Configuration.  Each call to this method
+     * returns a new instance of a Contex object; Context objects are not
+     * shared, however common state (ClassLoader, other Resources for the
+     * same configuration) may be so the Context itself can be fairly lightweight.
+     *
+     * @param overrideConfiguration A {@link Configuration} specifying what
+     * values to modify in the base Configuration of the original Context's
+     * resources.  If the base configuration changes (such as due to an
+     * orientation change), the resources of this context will also change except
+     * for those that have been explicitly overridden with a value here.
+     *
+     * @return A Context for the application.
+     */
+    public abstract Context createConfigurationContext(Configuration overrideConfiguration);
+
+    /**
      * Indicates whether this Context is restricted.
      *
      * @return True if this Context is restricted, false otherwise.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index ff4c9a1..fdf60ab 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -19,6 +19,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.DatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
@@ -533,6 +534,11 @@
     }
 
     @Override
+    public Context createConfigurationContext(Configuration overrideConfiguration) {
+        return mBase.createConfigurationContext(overrideConfiguration);
+    }
+
+    @Override
     public boolean isRestricted() {
         return mBase.isRestricted();
     }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d325186..0190555 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2279,24 +2279,24 @@
             "android.intent.action.PRE_BOOT_COMPLETED";
 
     /**
-     * Broadcast sent to the system when a user is added. Carries an extra EXTRA_USERID that has the
-     * userid of the new user.
+     * Broadcast sent to the system when a user is added. Carries an extra EXTRA_USER_HANDLE that has the
+     * userHandle of the new user.
      * @hide
      */
     public static final String ACTION_USER_ADDED =
             "android.intent.action.USER_ADDED";
 
     /**
-     * Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USERID that has
-     * the userid of the user.
+     * Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USER_HANDLE that has
+     * the userHandle of the user.
      * @hide
      */
     public static final String ACTION_USER_REMOVED =
             "android.intent.action.USER_REMOVED";
 
     /**
-     * Broadcast sent to the system when the user switches. Carries an extra EXTRA_USERID that has
-     * the userid of the user to become the current one.
+     * Broadcast sent to the system when the user switches. Carries an extra EXTRA_USER_HANDLE that has
+     * the userHandle of the user to become the current one.
      * @hide
      */
     public static final String ACTION_USER_SWITCHED =
@@ -2852,12 +2852,12 @@
         "android.intent.extra.LOCAL_ONLY";
 
     /**
-     * The userid carried with broadcast intents related to addition, removal and switching of users
+     * The userHandle carried with broadcast intents related to addition, removal and switching of users
      * - {@link #ACTION_USER_ADDED}, {@link #ACTION_USER_REMOVED} and {@link #ACTION_USER_SWITCHED}.
      * @hide
      */
-    public static final String EXTRA_USERID =
-            "android.intent.extra.user_id";
+    public static final String EXTRA_USER_HANDLE =
+            "android.intent.extra.user_handle";
 
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index 4db4bdc..1801488 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -27,6 +27,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.util.AndroidException;
 
 
@@ -223,6 +224,47 @@
     }
 
     /**
+     * Return the uid of the application that created this
+     * PendingIntent, that is the identity under which you will actually be
+     * sending the Intent.  The returned integer is supplied by the system, so
+     * that an application can not spoof its uid.
+     *
+     * @return The uid of the PendingIntent, or -1 if there is
+     * none associated with it.
+     */
+    public int getTargetUid() {
+        try {
+            return ActivityManagerNative.getDefault()
+                .getUidForIntentSender(mTarget);
+        } catch (RemoteException e) {
+            // Should never happen.
+            return -1;
+        }
+    }
+
+    /**
+     * Return the user handle of the application that created this
+     * PendingIntent, that is the user under which you will actually be
+     * sending the Intent.  The returned integer is supplied by the system, so
+     * that an application can not spoof its user.  See
+     * {@link android.os.Process#myUserHandle() Process.myUserHandle()} for
+     * more explanation of user handles.
+     *
+     * @return The user handle of the PendingIntent, or -1 if there is
+     * none associated with it.
+     */
+    public int getTargetUserHandle() {
+        try {
+            int uid = ActivityManagerNative.getDefault()
+                .getUidForIntentSender(mTarget);
+            return uid > 0 ? UserHandle.getUserId(uid) : -1;
+        } catch (RemoteException e) {
+            // Should never happen.
+            return -1;
+        }
+    }
+
+    /**
      * Comparison operator on two IntentSender objects, such that true
      * is returned then they both represent the same operation from the
      * same package.
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index e6303b9..ee075b4 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -52,7 +52,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.WorkSource;
 import android.provider.Settings;
@@ -174,7 +174,7 @@
                             Log.v(TAG, "Internal storage is low.");
                         }
                         mStorageIsLow = true;
-                        cancelActiveSync(null /* any account */, UserId.USER_ALL,
+                        cancelActiveSync(null /* any account */, UserHandle.USER_ALL,
                                 null /* any authority */);
                     } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -195,7 +195,7 @@
     private BroadcastReceiver mBackgroundDataSettingChanged = new BroadcastReceiver() {
         public void onReceive(Context context, Intent intent) {
             if (getConnectivityManager().getBackgroundDataSetting()) {
-                scheduleSync(null /* account */, UserId.USER_ALL, null /* authority */,
+                scheduleSync(null /* account */, UserHandle.USER_ALL, null /* authority */,
                         new Bundle(), 0 /* delay */,
                         false /* onlyThoseWithUnknownSyncableState */);
             }
@@ -287,7 +287,7 @@
             // a chance to set their syncable state.
 
             boolean onlyThoseWithUnkownSyncableState = justBootedUp;
-            scheduleSync(null, UserId.USER_ALL, null, null, 0 /* no delay */,
+            scheduleSync(null, UserHandle.USER_ALL, null, null, 0 /* no delay */,
                     onlyThoseWithUnkownSyncableState);
         }
     }
@@ -371,7 +371,7 @@
         mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() {
             public void onServiceChanged(SyncAdapterType type, boolean removed) {
                 if (!removed) {
-                    scheduleSync(null, UserId.USER_ALL, type.authority, null, 0 /* no delay */,
+                    scheduleSync(null, UserHandle.USER_ALL, type.authority, null, 0 /* no delay */,
                             false /* onlyThoseWithUnkownSyncableState */);
                 }
             }
@@ -517,7 +517,7 @@
         }
 
         AccountAndUser[] accounts;
-        if (requestedAccount != null && userId != UserId.USER_ALL) {
+        if (requestedAccount != null && userId != UserHandle.USER_ALL) {
             accounts = new AccountAndUser[] { new AccountAndUser(requestedAccount, userId) };
         } else {
             // if the accounts aren't configured yet then we can't support an account-less
@@ -895,7 +895,7 @@
     }
 
     private void onUserRemoved(Intent intent) {
-        int userId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
+        int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
         if (userId == -1) return;
 
         // Clean up the storage engine database
@@ -2180,7 +2180,7 @@
                         }
                     }
                     // check if the userid matches
-                    if (userId != UserId.USER_ALL
+                    if (userId != UserHandle.USER_ALL
                             && userId != activeSyncContext.mSyncOperation.userId) {
                         continue;
                     }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index d906401..ac75040 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -28,7 +28,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.PatternMatcher;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.AttributeSet;
 import android.util.Base64;
 import android.util.DisplayMetrics;
@@ -249,7 +249,7 @@
 
         return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
                 grantedPermissions, false, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
-                UserId.getCallingUserId());
+                UserHandle.getCallingUserId());
     }
 
     /**
@@ -263,7 +263,7 @@
             HashSet<String> grantedPermissions, boolean stopped, int enabledState) {
 
         return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
-                grantedPermissions, stopped, enabledState, UserId.getCallingUserId());
+                grantedPermissions, stopped, enabledState, UserHandle.getCallingUserId());
     }
 
     public static PackageInfo generatePackageInfo(PackageParser.Package p,
@@ -3478,7 +3478,7 @@
 
     public static ApplicationInfo generateApplicationInfo(Package p, int flags, boolean stopped,
             int enabledState) {
-        return generateApplicationInfo(p, flags, stopped, enabledState, UserId.getCallingUserId());
+        return generateApplicationInfo(p, flags, stopped, enabledState, UserHandle.getCallingUserId());
     }
 
     public static ApplicationInfo generateApplicationInfo(Package p, int flags,
@@ -3508,7 +3508,7 @@
         // Make shallow copy so we can store the metadata/libraries safely
         ApplicationInfo ai = new ApplicationInfo(p.applicationInfo);
         if (userId != 0) {
-            ai.uid = UserId.getUid(userId, ai.uid);
+            ai.uid = UserHandle.getUid(userId, ai.uid);
             ai.dataDir = PackageManager.getDataDirForUser(userId, ai.packageName);
         }
         if ((flags & PackageManager.GET_META_DATA) != 0) {
@@ -3616,7 +3616,7 @@
             int enabledState, int userId) {
         if (s == null) return null;
         if (!copyNeeded(flags, s.owner, enabledState, s.metaData)
-                && userId == UserId.getUserId(s.info.applicationInfo.uid)) {
+                && userId == UserHandle.getUserId(s.info.applicationInfo.uid)) {
             return s.info;
         }
         // Make shallow copies so we can store the metadata safely
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 638e273..060a235 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -53,6 +53,7 @@
     public static final int FLAG_RESTRICTED = 0x00000008;
 
     public int id;
+    public int serialNumber;
     public String name;
     public String iconPath;
     public int flags;
@@ -88,6 +89,7 @@
         iconPath = orig.iconPath;
         id = orig.id;
         flags = orig.flags;
+        serialNumber = orig.serialNumber;
     }
 
     @Override
@@ -104,6 +106,7 @@
         dest.writeString(name);
         dest.writeString(iconPath);
         dest.writeInt(flags);
+        dest.writeInt(serialNumber);
     }
 
     public static final Parcelable.Creator<UserInfo> CREATOR
@@ -121,5 +124,6 @@
         name = source.readString();
         iconPath = source.readString();
         flags = source.readInt();
+        serialNumber = source.readInt();
     }
 }
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index ea13a2a..52b6498 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -35,6 +35,9 @@
  * <pre>Configuration config = getResources().getConfiguration();</pre>
  */
 public final class Configuration implements Parcelable, Comparable<Configuration> {
+    /** @hide */
+    public static final Configuration EMPTY = new Configuration();
+
     /**
      * Current user preference for the scaling factor for fonts, relative
      * to the base density scaling.
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index d2af3e9..7559f1e 100755
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1121,8 +1121,8 @@
          * Return a StyledAttributes holding the values defined by
          * <var>Theme</var> which are listed in <var>attrs</var>.
          * 
-         * <p>Be sure to call StyledAttributes.recycle() when you are done with
-         * the array.
+         * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
+         * with the array.
          * 
          * @param attrs The desired attributes.
          *
@@ -1149,8 +1149,8 @@
          * Return a StyledAttributes holding the values defined by the style
          * resource <var>resid</var> which are listed in <var>attrs</var>.
          * 
-         * <p>Be sure to call StyledAttributes.recycle() when you are done with
-         * the array.
+         * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
+         * with the array.
          * 
          * @param resid The desired style resource.
          * @param attrs The desired attributes in the style.
@@ -1209,8 +1209,8 @@
          * AttributeSet specifies a style class (through the "style" attribute),
          * that style will be applied on top of the base attributes it defines.
          * 
-         * <p>Be sure to call StyledAttributes.recycle() when you are done with
-         * the array.
+         * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
+         * with the array.
          * 
          * <p>When determining the final value of a particular attribute, there
          * are four inputs that come into play:</p>
@@ -1435,9 +1435,12 @@
             int configChanges = 0xfffffff;
             if (config != null) {
                 mTmpConfig.setTo(config);
+                int density = config.densityDpi;
+                if (density == Configuration.DENSITY_DPI_UNDEFINED) {
+                    density = mMetrics.noncompatDensityDpi;
+                }
                 if (mCompatibilityInfo != null) {
-                    mCompatibilityInfo.applyToConfiguration(mMetrics.noncompatDensityDpi,
-                            mTmpConfig);
+                    mCompatibilityInfo.applyToConfiguration(density, mTmpConfig);
                 }
                 if (mTmpConfig.locale == null) {
                     mTmpConfig.locale = Locale.getDefault();
@@ -1448,6 +1451,10 @@
             if (mConfiguration.locale == null) {
                 mConfiguration.locale = Locale.getDefault();
             }
+            if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
+                mMetrics.densityDpi = mConfiguration.densityDpi;
+                mMetrics.density = mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+            }
             mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
 
             String locale = null;
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index 281b843..7f3b6b9 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -691,7 +691,7 @@
     }
 
     /**
-     * Give back a previously retrieved StyledAttributes, for later re-use.
+     * Give back a previously retrieved array, for later re-use.
      */
     public void recycle() {
         synchronized (mResources.mTmpValue) {
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 3c70dc6..e0c9d2c 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -26,7 +26,7 @@
  * @see SensorEvent
  *
  */
-public class Sensor {
+public final class Sensor {
 
     /**
      * A constant describing an accelerometer sensor type. See
@@ -202,4 +202,11 @@
         mMaxRange = max;
         mResolution = res;
     }
+
+    @Override
+    public String toString() {
+        return "{Sensor name=\"" + mName + "\", vendor=\"" + mVendor + "\", version=" + mVersion
+                + ", type=" + mType + ", maxRange=" + mMaxRange + ", resolution=" + mResolution
+                + ", power=" + mPower + ", minDelay=" + mMinDelay + "}";
+    }
 }
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 7b51119..5d40456 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -82,7 +82,7 @@
      * @hide
      */
     public static final int getOrigCallingUid() {
-        if (UserId.MU_ENABLED) {
+        if (UserHandle.MU_ENABLED) {
             return getOrigCallingUidNative();
         } else {
             return getCallingUid();
@@ -97,7 +97,7 @@
      * @hide
      */
     public static final int getOrigCallingUser() {
-        return UserId.getUserId(getOrigCallingUid());
+        return UserHandle.getUserId(getOrigCallingUid());
     }
 
     /**
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 6c1445d..7c103aa 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -28,9 +28,6 @@
 import java.util.zip.CRC32;
 import java.util.zip.CheckedInputStream;
 
-import libcore.io.Os;
-import libcore.io.StructStat;
-
 /**
  * Tools for managing files.  Not for public consumption.
  * @hide
@@ -50,58 +47,12 @@
     public static final int S_IROTH = 00004;
     public static final int S_IWOTH = 00002;
     public static final int S_IXOTH = 00001;
-    
-    
-    /**
-     * File status information. This class maps directly to the POSIX stat structure.
-     * @deprecated use {@link StructStat} instead.
-     * @hide
-     */
-    @Deprecated
-    public static final class FileStatus {
-        public int dev;
-        public int ino;
-        public int mode;
-        public int nlink;
-        public int uid;
-        public int gid;
-        public int rdev;
-        public long size;
-        public int blksize;
-        public long blocks;
-        public long atime;
-        public long mtime;
-        public long ctime;
-    }
-    
-    /**
-     * Get the status for the given path. This is equivalent to the POSIX stat(2) system call. 
-     * @param path The path of the file to be stat'd.
-     * @param status Optional argument to fill in. It will only fill in the status if the file
-     * exists. 
-     * @return true if the file exists and false if it does not exist. If you do not have 
-     * permission to stat the file, then this method will return false.
-     * @deprecated use {@link Os#stat(String)} instead.
-     */
-    @Deprecated
-    public static boolean getFileStatus(String path, FileStatus status) {
-        StrictMode.noteDiskRead();
-        return getFileStatusNative(path, status);
-    }
-
-    private static native boolean getFileStatusNative(String path, FileStatus status);
 
     /** Regular expression for safe filenames: no spaces or metacharacters */
     private static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+");
 
     public static native int setPermissions(String file, int mode, int uid, int gid);
 
-    /**
-     * @deprecated use {@link Os#stat(String)} instead.
-     */
-    @Deprecated
-    public static native int getPermissions(String file, int[] outPermissions);
-
     public static native int setUMask(int mask);
 
     /** returns the FAT file system volume ID for the volume mounted 
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index b892c81..7785368 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -123,6 +123,7 @@
         }
         mQueue = mLooper.mQueue;
         mCallback = null;
+        mAsynchronous = false;
     }
 
     /**
@@ -147,6 +148,7 @@
         }
         mQueue = mLooper.mQueue;
         mCallback = callback;
+        mAsynchronous = false;
     }
 
     /**
@@ -156,6 +158,7 @@
         mLooper = looper;
         mQueue = looper.mQueue;
         mCallback = null;
+        mAsynchronous = false;
     }
 
     /**
@@ -166,6 +169,31 @@
         mLooper = looper;
         mQueue = looper.mQueue;
         mCallback = callback;
+        mAsynchronous = false;
+    }
+
+    /**
+     * Use the provided queue instead of the default one and take a callback
+     * interface in which to handle messages.  Also set whether the handler
+     * should be asynchronous.
+     *
+     * Handlers are synchronous by default unless this constructor is used to make
+     * one that is strictly asynchronous.
+     *
+     * Asynchronous messages represent interrupts or events that do not require global ordering
+     * with represent to synchronous messages.  Asynchronous messages are not subject to
+     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
+     *
+     * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
+     * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
+     *
+     * @hide
+     */
+    public Handler(Looper looper, Callback callback, boolean async) {
+        mLooper = looper;
+        mQueue = looper.mQueue;
+        mCallback = callback;
+        mAsynchronous = async;
     }
 
     /**
@@ -464,20 +492,15 @@
      *         the looper is quit before the delivery time of the message
      *         occurs then the message will be dropped.
      */
-    public boolean sendMessageAtTime(Message msg, long uptimeMillis)
-    {
-        boolean sent = false;
+    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
         MessageQueue queue = mQueue;
-        if (queue != null) {
-            msg.target = this;
-            sent = queue.enqueueMessage(msg, uptimeMillis);
-        }
-        else {
+        if (queue == null) {
             RuntimeException e = new RuntimeException(
-                this + " sendMessageAtTime() called with no mQueue");
+                    this + " sendMessageAtTime() called with no mQueue");
             Log.w("Looper", e.getMessage(), e);
+            return false;
         }
-        return sent;
+        return enqueueMessage(queue, msg, uptimeMillis);
     }
 
     /**
@@ -492,20 +515,23 @@
      *         message queue.  Returns false on failure, usually because the
      *         looper processing the message queue is exiting.
      */
-    public final boolean sendMessageAtFrontOfQueue(Message msg)
-    {
-        boolean sent = false;
+    public final boolean sendMessageAtFrontOfQueue(Message msg) {
         MessageQueue queue = mQueue;
-        if (queue != null) {
-            msg.target = this;
-            sent = queue.enqueueMessage(msg, 0);
-        }
-        else {
+        if (queue == null) {
             RuntimeException e = new RuntimeException(
                 this + " sendMessageAtTime() called with no mQueue");
             Log.w("Looper", e.getMessage(), e);
+            return false;
         }
-        return sent;
+        return enqueueMessage(queue, msg, 0);
+    }
+
+    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
+        msg.target = this;
+        if (mAsynchronous) {
+            msg.setAsynchronous(true);
+        }
+        return queue.enqueueMessage(msg, uptimeMillis);
     }
 
     /**
@@ -618,5 +644,6 @@
     final MessageQueue mQueue;
     final Looper mLooper;
     final Callback mCallback;
+    final boolean mAsynchronous;
     IMessenger mMessenger;
 }
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 270e9be..7aee644 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -23,27 +23,33 @@
 
 interface IPowerManager
 {
-    // WARNING: changes in acquireWakeLock() signature must be reflected in IPowerManager.cpp/h
-    void acquireWakeLock(int flags, IBinder lock, String tag, in WorkSource ws);
-    void updateWakeLockWorkSource(IBinder lock, in WorkSource ws);
-    void goToSleep(long time);
-    void goToSleepWithReason(long time, int reason);
-    // WARNING: changes in releaseWakeLock() signature must be reflected in IPowerManager.cpp/h
+    // WARNING: The first two methods must remain the first two methods because their
+    // transaction numbers must not change unless IPowerManager.cpp is also updated.
+    void acquireWakeLock(IBinder lock, int flags, String tag, in WorkSource ws);
     void releaseWakeLock(IBinder lock, int flags);
-    void userActivity(long when, boolean noChangeLights);
-    void userActivityWithForce(long when, boolean noChangeLights, boolean force);
-    void clearUserActivityTimeout(long now, long timeout);
-    void setPokeLock(int pokey, IBinder lock, String tag);
-    int getSupportedWakeLockFlags();
-    void setStayOnSetting(int val);
-    void setMaximumScreenOffTimeount(int timeMs);
-    void preventScreenOn(boolean prevent);
+
+    void updateWakeLockWorkSource(IBinder lock, in WorkSource ws);
+    boolean isWakeLockLevelSupported(int level);
+
+    void userActivity(long time, int event, int flags);
+    void wakeUp(long time);
+    void goToSleep(long time, int reason);
+
     boolean isScreenOn();
     void reboot(String reason);
     void crash(String message);
 
-    // sets the brightness of the backlights (screen, keyboard, button) 0-255
-    void setBacklightBrightness(int brightness);
+    void clearUserActivityTimeout(long now, long timeout);
+    void setPokeLock(int pokey, IBinder lock, String tag);
+    void setStayOnSetting(int val);
+    void setMaximumScreenOffTimeoutFromDeviceAdmin(int timeMs);
+    void preventScreenOn(boolean prevent);
+
+    // temporarily overrides the screen brightness settings to allow the user to
+    // see the effect of a settings change without applying it immediately
+    void setTemporaryScreenBrightnessSettingOverride(int brightness);
+    void setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(float adj);
+
+    // sets the attention light (used by phone app only)
     void setAttentionLight(boolean on, int color);
-    void setAutoBrightnessAdjustment(float adj);
 }
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index cb1b962..c7a8493 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -33,4 +33,6 @@
     void setGuestEnabled(boolean enable);
     boolean isGuestEnabled();
     void wipeUser(int userHandle);
+    int getUserSerialNumber(int userHandle);
+    int getUserHandle(int userSerialNumber);
 }
diff --git a/core/java/android/os/LocalPowerManager.java b/core/java/android/os/LocalPowerManager.java
index 09336c7..519315c 100644
--- a/core/java/android/os/LocalPowerManager.java
+++ b/core/java/android/os/LocalPowerManager.java
@@ -18,25 +18,11 @@
 
 /** @hide */
 public interface LocalPowerManager {
+    // FIXME: Replace poke locks with something else.
+
     public static final int POKE_LOCK_IGNORE_TOUCH_EVENTS = 0x1;
 
     public static final int POKE_LOCK_SHORT_TIMEOUT = 0x2;
     public static final int POKE_LOCK_MEDIUM_TIMEOUT = 0x4;
     public static final int POKE_LOCK_TIMEOUT_MASK = 0x6;
-
-    void goToSleep(long time);
-    
-    // notify power manager when keyboard is opened/closed
-    void setKeyboardVisibility(boolean visible);
-
-    // when the keyguard is up, it manages the power state, and userActivity doesn't do anything.
-    void enableUserActivity(boolean enabled);
-
-    // the same as the method on PowerManager
-    void userActivity(long time, boolean noChangeLights, int eventType);
-
-    boolean isScreenOn();
-
-    void setScreenBrightnessOverride(int brightness);
-    void setButtonBrightnessOverride(int brightness);
 }
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index a757303..efdbcac 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.content.Context;
 import android.util.Log;
 
 /**
@@ -42,8 +43,8 @@
  * wl.release();
  * }
  * </p><p>
- * The following flags are defined, with varying effects on system power.
- * <i>These flags are mutually exclusive - you may only specify one of them.</i>
+ * The following wake lock levels are defined, with varying effects on system power.
+ * <i>These levels are mutually exclusive - you may only specify one of them.</i>
  *
  * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
  *     <thead>
@@ -177,7 +178,7 @@
     /**
      * Wake lock level: Turns the screen off when the proximity sensor activates.
      * <p>
-     * Since not all devices have proximity sensors, use {@link #getSupportedWakeLockFlags}
+     * Since not all devices have proximity sensors, use {@link #isWakeLockLevelSupported}
      * to determine whether this wake lock level is supported.
      * </p>
      *
@@ -227,29 +228,24 @@
     public static final int WAIT_FOR_PROXIMITY_NEGATIVE = 1;
 
     /**
-     * Brightness value to use when battery is low.
-     * @hide
-     */
-    public static final int BRIGHTNESS_LOW_BATTERY = 10;
-
-    /**
      * Brightness value for fully on.
      * @hide
      */
     public static final int BRIGHTNESS_ON = 255;
 
     /**
-     * Brightness value for dim backlight.
-     * @hide
-     */
-    public static final int BRIGHTNESS_DIM = 20;
-
-    /**
      * Brightness value for fully off.
      * @hide
      */
     public static final int BRIGHTNESS_OFF = 0;
 
+    /**
+     * A nominal default brightness value.
+     * Use {@link #getDefaultScreenBrightnessSetting()} instead.
+     * @hide
+     */
+    private static final int BRIGHTNESS_DEFAULT = 102;
+
     // Note: Be sure to update android.os.BatteryStats and PowerManager.h
     // if adding or modifying user activity event constants.
 
@@ -271,18 +267,82 @@
      */
     public static final int USER_ACTIVITY_EVENT_TOUCH = 2;
 
+    /**
+     * User activity flag: Do not restart the user activity timeout or brighten
+     * the display in response to user activity if it is already dimmed.
+     * @hide
+     */
+    public static final int USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS = 1 << 0;
+
+    /**
+     * Special wake lock tag used for the wake lock in the Window Manager that handles the
+     * {@link android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON} flag.
+     * @hide
+     */
+    public static final String KEEP_SCREEN_ON_FLAG_TAG = "KEEP_SCREEN_ON_FLAG";
+
+    /**
+     * Go to sleep reason code: Going to sleep due by user request.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_USER = 0;
+
+    /**
+     * Go to sleep reason code: Going to sleep due by request of the
+     * device administration policy.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_DEVICE_ADMIN = 1;
+
+    /**
+     * Go to sleep reason code: Going to sleep due to a screen timeout.
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_TIMEOUT = 2;
+
+    final Context mContext;
     final IPowerManager mService;
     final Handler mHandler;
 
     /**
      * {@hide}
      */
-    public PowerManager(IPowerManager service, Handler handler) {
+    public PowerManager(Context context, IPowerManager service, Handler handler) {
+        mContext = context;
         mService = service;
         mHandler = handler;
     }
 
     /**
+     * Gets the minimum supported screen brightness setting.
+     * The screen may be allowed to become dimmer than this value but
+     * this is the minimum value that can be set by the user.
+     * @hide
+     */
+    public int getMinimumScreenBrightnessSetting() {
+        return mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_screenBrightnessDim);
+    }
+
+    /**
+     * Gets the maximum supported screen brightness setting.
+     * The screen may be allowed to become dimmer than this value but
+     * this is the maximum value that can be set by the user.
+     * @hide
+     */
+    public int getMaximumScreenBrightnessSetting() {
+        return BRIGHTNESS_ON;
+    }
+
+    /**
+     * Gets the default screen brightness setting.
+     * @hide
+     */
+    public int getDefaultScreenBrightnessSetting() {
+        return BRIGHTNESS_DEFAULT;
+    }
+
+    /**
      * Creates a new wake lock with the specified level and flags.
      * <p>
      * The {@code levelAndFlags} parameter specifies a wake lock level and optional flags
@@ -360,8 +420,10 @@
     /**
      * Notifies the power manager that user activity happened.
      * <p>
-     * Turns the device from whatever state it's in to full on, and resets
-     * the auto-off timer.
+     * Resets the auto-off timer and brightens the screen if the device
+     * is not asleep.  This is what happens normally when a key or the touch
+     * screen is pressed or when some other user activity occurs.
+     * This method does not wake up the device if it has been put to sleep.
      * </p><p>
      * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
      * </p>
@@ -375,10 +437,14 @@
      * We want the device to stay on while the button is down, but we're about
      * to turn off the screen so we don't want the keyboard backlight to turn on again.
      * Otherwise the lights flash on and then off and it looks weird.
+     *
+     * @see #wakeUp
+     * @see #goToSleep
      */
     public void userActivity(long when, boolean noChangeLights) {
         try {
-            mService.userActivity(when, noChangeLights);
+            mService.userActivity(when, USER_ACTIVITY_EVENT_OTHER,
+                    noChangeLights ? USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS : 0);
         } catch (RemoteException e) {
         }
     }
@@ -386,8 +452,8 @@
    /**
      * Forces the device to go to sleep.
      * <p>
-     * Overrides all the wake locks that are held.  This is what happen when the power
-     * key is pressed to turn off the screen.
+     * Overrides all the wake locks that are held.
+     * This is what happens when the power key is pressed to turn off the screen.
      * </p><p>
      * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
      * </p>
@@ -396,10 +462,37 @@
      * {@link SystemClock#uptimeMillis()} time base.  This timestamp is used to correctly
      * order the user activity with other power management functions.  It should be set
      * to the timestamp of the input event that caused the request to go to sleep.
+     *
+     * @see #userActivity
+     * @see #wakeUp
      */
     public void goToSleep(long time) {
         try {
-            mService.goToSleep(time);
+            mService.goToSleep(time, GO_TO_SLEEP_REASON_USER);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Forces the device to wake up from sleep.
+     * <p>
+     * If the device is currently asleep, wakes it up, otherwise does nothing.
+     * This is what happens when the power key is pressed to turn on the screen.
+     * </p><p>
+     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+     * </p>
+     *
+     * @param time The time when the request to wake up was issued, in the
+     * {@link SystemClock#uptimeMillis()} time base.  This timestamp is used to correctly
+     * order the user activity with other power management functions.  It should be set
+     * to the timestamp of the input event that caused the request to wake up.
+     *
+     * @see #userActivity
+     * @see #goToSleep
+     */
+    public void wakeUp(long time) {
+        try {
+            mService.wakeUp(time);
         } catch (RemoteException e) {
         }
     }
@@ -416,34 +509,24 @@
      */
     public void setBacklightBrightness(int brightness) {
         try {
-            mService.setBacklightBrightness(brightness);
+            mService.setTemporaryScreenBrightnessSettingOverride(brightness);
         } catch (RemoteException e) {
         }
     }
 
    /**
-     * Returns the set of wake lock levels and flags for {@link #newWakeLock}
-     * that are supported on the device.
-     * <p>
-     * For example, to test to see if the {@link #PROXIMITY_SCREEN_OFF_WAKE_LOCK}
-     * is supported:
-     * {@samplecode
-     * PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
-     * int supportedFlags = pm.getSupportedWakeLockFlags();
-     * boolean proximitySupported = ((supportedFlags & PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)
-     *         == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK);
-     * }
-     * </p>
+     * Returns true if the specified wake lock level is supported.
      *
-     * @return The set of supported WakeLock flags.
+     * @param level The wake lock level to check.
+     * @return True if the specified wake lock level is supported.
      *
      * {@hide}
      */
-    public int getSupportedWakeLockFlags() {
+    public boolean isWakeLockLevelSupported(int level) {
         try {
-            return mService.getSupportedWakeLockFlags();
+            return mService.isWakeLockLevelSupported(level);
         } catch (RemoteException e) {
-            return 0;
+            return false;
         }
     }
 
@@ -593,7 +676,7 @@
                 // been explicitly released by the keyguard.
                 mHandler.removeCallbacks(mReleaser);
                 try {
-                    mService.acquireWakeLock(mFlags, mToken, mTag, mWorkSource);
+                    mService.acquireWakeLock(mToken, mFlags, mTag, mWorkSource);
                 } catch (RemoteException e) {
                 }
                 mHeld = true;
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 93860aa..0553384 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -376,12 +376,13 @@
     public static final ProcessStartResult start(final String processClass,
                                   final String niceName,
                                   int uid, int gid, int[] gids,
-                                  int debugFlags, int targetSdkVersion,
+                                  int debugFlags, int mountExternal,
+                                  int targetSdkVersion,
                                   String seInfo,
                                   String[] zygoteArgs) {
         try {
             return startViaZygote(processClass, niceName, uid, gid, gids,
-                    debugFlags, targetSdkVersion, seInfo, zygoteArgs);
+                    debugFlags, mountExternal, targetSdkVersion, seInfo, zygoteArgs);
         } catch (ZygoteStartFailedEx ex) {
             Log.e(LOG_TAG,
                     "Starting VM process through Zygote failed");
@@ -553,7 +554,8 @@
                                   final String niceName,
                                   final int uid, final int gid,
                                   final int[] gids,
-                                  int debugFlags, int targetSdkVersion,
+                                  int debugFlags, int mountExternal,
+                                  int targetSdkVersion,
                                   String seInfo,
                                   String[] extraArgs)
                                   throws ZygoteStartFailedEx {
@@ -580,6 +582,11 @@
             if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
                 argsForZygote.add("--enable-assert");
             }
+            if (mountExternal == Zygote.MOUNT_EXTERNAL_SINGLEUSER) {
+                argsForZygote.add("--mount-external-singleuser");
+            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER) {
+                argsForZygote.add("--mount-external-multiuser");
+            }
             argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
 
             //TODO optionally enable debuger
@@ -654,7 +661,7 @@
      * distinct apps running under it each with their own uid.
      */
     public static final int myUserHandle() {
-        return UserId.getUserId(myUid());
+        return UserHandle.getUserId(myUid());
     }
 
     /**
@@ -662,7 +669,7 @@
      * @hide
      */
     public static final boolean isIsolated() {
-        int uid = UserId.getAppId(myUid());
+        int uid = UserHandle.getAppId(myUid());
         return uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID;
     }
 
diff --git a/core/java/android/os/SELinux.java b/core/java/android/os/SELinux.java
index 90cfa37..c05a974 100644
--- a/core/java/android/os/SELinux.java
+++ b/core/java/android/os/SELinux.java
@@ -1,5 +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 android.os;
 
+import android.util.Slog;
+
+import java.io.IOException;
+import java.io.File;
 import java.io.FileDescriptor;
 
 /**
@@ -9,6 +29,8 @@
  */
 public class SELinux {
 
+    private static final String TAG = "SELinux";
+
     /**
      * Determine whether SELinux is disabled or enabled.
      * @return a boolean indicating whether SELinux is enabled.
@@ -102,4 +124,53 @@
      * @return a boolean indicating whether permission was granted.
      */
     public static final native boolean checkSELinuxAccess(String scon, String tcon, String tclass, String perm);
+
+    /**
+     * Restores a file to its default SELinux security context.
+     * If the system is not compiled with SELinux, then {@code true}
+     * is automatically returned.
+     * If SELinux is compiled in, but disabled, then {@code true} is
+     * returned.
+     *
+     * @param pathname The pathname of the file to be relabeled.
+     * @return a boolean indicating whether the relabeling succeeded.
+     * @exception NullPointerException if the pathname is a null object.
+     */
+    public static boolean restorecon(String pathname) throws NullPointerException {
+        if (pathname == null) { throw new NullPointerException(); }
+        return native_restorecon(pathname);
+    }
+
+    /**
+     * Restores a file to its default SELinux security context.
+     * If the system is not compiled with SELinux, then {@code true}
+     * is automatically returned.
+     * If SELinux is compiled in, but disabled, then {@code true} is
+     * returned.
+     *
+     * @param pathname The pathname of the file to be relabeled.
+     * @return a boolean indicating whether the relabeling succeeded.
+     */
+    private static native boolean native_restorecon(String pathname);
+
+    /**
+     * Restores a file to its default SELinux security context.
+     * If the system is not compiled with SELinux, then {@code true}
+     * is automatically returned.
+     * If SELinux is compiled in, but disabled, then {@code true} is
+     * returned.
+     *
+     * @param file The File object representing the path to be relabeled.
+     * @return a boolean indicating whether the relabeling succeeded.
+     * @exception NullPointerException if the file is a null object.
+     */
+    public static boolean restorecon(File file) throws NullPointerException {
+        try {
+            return native_restorecon(file.getCanonicalPath());
+        } catch (IOException e) {
+            Slog.e(TAG, "Error getting canonical path. Restorecon failed for " +
+                   file.getPath(), e);
+            return false;
+        }
+    }
 }
diff --git a/core/java/android/os/StatFs.java b/core/java/android/os/StatFs.java
index 912bfdf4..ca7fdba 100644
--- a/core/java/android/os/StatFs.java
+++ b/core/java/android/os/StatFs.java
@@ -16,59 +16,77 @@
 
 package android.os;
 
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+import libcore.io.StructStatFs;
+
 /**
- * Retrieve overall information about the space on a filesystem.  This is a
- * Wrapper for Unix statfs().
+ * Retrieve overall information about the space on a filesystem. This is a
+ * wrapper for Unix statfs().
  */
 public class StatFs {
-    /**
-     * Construct a new StatFs for looking at the stats of the
-     * filesystem at <var>path</var>.  Upon construction, the stat of
-     * the file system will be performed, and the values retrieved available
-     * from the methods on this class.
-     * 
-     * @param path A path in the desired file system to state.
-     */
-    public StatFs(String path) { native_setup(path); }
-    
-    /**
-     * Perform a restat of the file system referenced by this object.  This
-     * is the same as re-constructing the object with the same file system
-     * path, and the new stat values are available upon return.
-     */
-    public void restat(String path) { native_restat(path); }
-
-    @Override
-    protected void finalize() { native_finalize(); }
+    private StructStatFs mStat;
 
     /**
-     * The size, in bytes, of a block on the file system.  This corresponds
-     * to the Unix statfs.f_bsize field.
+     * Construct a new StatFs for looking at the stats of the filesystem at
+     * {@code path}. Upon construction, the stat of the file system will be
+     * performed, and the values retrieved available from the methods on this
+     * class.
+     *
+     * @param path path in the desired file system to stat.
      */
-    public native int getBlockSize();
+    public StatFs(String path) {
+        mStat = doStat(path);
+    }
+
+    private static StructStatFs doStat(String path) {
+        try {
+            return Libcore.os.statfs(path);
+        } catch (ErrnoException e) {
+            throw new IllegalArgumentException("Invalid path: " + path, e);
+        }
+    }
 
     /**
-     * The total number of blocks on the file system.  This corresponds
-     * to the Unix statfs.f_blocks field.
+     * Perform a restat of the file system referenced by this object. This is
+     * the same as re-constructing the object with the same file system path,
+     * and the new stat values are available upon return.
      */
-    public native int getBlockCount();
+    public void restat(String path) {
+        mStat = doStat(path);
+    }
+
+    /**
+     * The size, in bytes, of a block on the file system. This corresponds to
+     * the Unix {@code statfs.f_bsize} field.
+     */
+    public int getBlockSize() {
+        return (int) mStat.f_bsize;
+    }
+
+    /**
+     * The total number of blocks on the file system. This corresponds to the
+     * Unix {@code statfs.f_blocks} field.
+     */
+    public int getBlockCount() {
+        return (int) mStat.f_blocks;
+    }
 
     /**
      * The total number of blocks that are free on the file system, including
-     * reserved blocks (that are not available to normal applications).  This
-     * corresponds to the Unix statfs.f_bfree field.  Most applications will
-     * want to use {@link #getAvailableBlocks()} instead.
+     * reserved blocks (that are not available to normal applications). This
+     * corresponds to the Unix {@code statfs.f_bfree} field. Most applications
+     * will want to use {@link #getAvailableBlocks()} instead.
      */
-    public native int getFreeBlocks();
+    public int getFreeBlocks() {
+        return (int) mStat.f_bfree;
+    }
 
     /**
      * The number of blocks that are free on the file system and available to
-     * applications.  This corresponds to the Unix statfs.f_bavail field.
+     * applications. This corresponds to the Unix {@code statfs.f_bavail} field.
      */
-    public native int getAvailableBlocks();    
-    
-    private int mNativeContext;
-    private native void native_restat(String path);
-    private native void native_setup(String path);
-    private native void native_finalize();
+    public int getAvailableBlocks() {
+        return (int) mStat.f_bavail;
+    }
 }
diff --git a/core/java/android/os/UserId.java b/core/java/android/os/UserHandle.java
similarity index 93%
rename from core/java/android/os/UserId.java
rename to core/java/android/os/UserHandle.java
index 7e611df..577a8c6 100644
--- a/core/java/android/os/UserId.java
+++ b/core/java/android/os/UserHandle.java
@@ -17,9 +17,10 @@
 package android.os;
 
 /**
+ * Representation of a user on the device.
  * @hide
  */
-public final class UserId {
+public final class UserHandle {
     /**
      * Range of IDs allocated for a user.
      *
@@ -33,6 +34,8 @@
     /** A user id to indicate the currently active user */
     public static final int USER_CURRENT = -2;
 
+    /** A user id constant to indicate the "owner" user of the device */
+    public static final int USER_OWNER = 0;
 
     /**
      * Enable multi-user related side effects. Set this to false if there are problems with single
@@ -68,7 +71,7 @@
 
     public static boolean isApp(int uid) {
         if (uid > 0) {
-            uid = UserId.getAppId(uid);
+            uid = UserHandle.getAppId(uid);
             return uid >= Process.FIRST_APPLICATION_UID && uid <= Process.LAST_APPLICATION_UID;
         } else {
             return false;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 9c73392..93da44a 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -209,4 +209,40 @@
     public int getMaxSupportedUsers() {
         return mContext.getResources().getInteger(R.integer.config_multiuserMaximumUsers);
     }
+
+    /**
+     * Returns a serial number on this device for a given userHandle. User handles can be recycled
+     * when deleting and creating users, but serial numbers are not reused until the device is wiped.
+     * @param userHandle
+     * @return a serial number associated with that user, or -1 if the userHandle is not valid.
+     * @hide
+     */
+    public int getUserSerialNumber(int userHandle) {
+        try {
+            return mService.getUserSerialNumber(userHandle);
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not get serial number for user " + userHandle);
+        }
+        return -1;
+    }
+
+    /**
+     * Returns a userHandle on this device for a given user serial number. User handles can be
+     * recycled when deleting and creating users, but serial numbers are not reused until the device
+     * is wiped.
+     * @param userSerialNumber
+     * @return the userHandle associated with that user serial number, or -1 if the serial number
+     * is not valid.
+     * @hide
+     */
+    public int getUserHandle(int userSerialNumber) {
+        try {
+            return mService.getUserHandle(userSerialNumber);
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not get userHandle for user " + userSerialNumber);
+        }
+        return -1;
+    }
+
+
 }
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index a85f4fa..ba77df7 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -1,5 +1,9 @@
 package android.os;
 
+import com.android.internal.util.ArrayUtils;
+
+import java.util.Arrays;
+
 /**
  * Describes the source of some work that may be done by someone else.
  * Currently the public representation of what a work source is is not
@@ -313,6 +317,20 @@
         dest.writeIntArray(mUids);
     }
 
+    @Override
+    public String toString() {
+        StringBuilder result = new StringBuilder();
+        result.append("{WorkSource: uids=[");
+        for (int i = 0; i < mNum; i++) {
+            if (i != 0) {
+                result.append(", ");
+            }
+            result.append(mUids[i]);
+        }
+        result.append("]}");
+        return result.toString();
+    }
+
     public static final Parcelable.Creator<WorkSource> CREATOR
             = new Parcelable.Creator<WorkSource>() {
         public WorkSource createFromParcel(Parcel in) {
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 22b68bc..5dca67f 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -17,9 +17,6 @@
 
 package android.provider;
 
-import com.android.internal.telephony.CallerInfo;
-import com.android.internal.telephony.PhoneConstants;
-
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -30,6 +27,9 @@
 import android.provider.ContactsContract.DataUsageFeedback;
 import android.text.TextUtils;
 
+import com.android.internal.telephony.CallerInfo;
+import com.android.internal.telephony.PhoneConstants;
+
 /**
  * The CallLog provider contains information about placed and received calls.
  */
@@ -59,6 +59,20 @@
                 Uri.parse("content://call_log/calls/filter");
 
         /**
+         * Query parameter used to limit the number of call logs returned.
+         * <p>
+         * TYPE: integer
+         */
+        public static final String LIMIT_PARAM_KEY = "limit";
+
+        /**
+         * Query parameter used to specify the starting record to return.
+         * <p>
+         * TYPE: integer
+         */
+        public static final String OFFSET_PARAM_KEY = "offset";
+
+        /**
          * An optional URI parameter which instructs the provider to allow the operation to be
          * applied to voicemail records as well.
          * <p>
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 01252f0..6dbba46 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -41,7 +41,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.speech.tts.TextToSpeech;
 import android.text.TextUtils;
 import android.util.AndroidException;
@@ -1381,7 +1381,9 @@
 
         /**
          * Whether or not to dim the screen. 0=no  1=yes
+         * @deprecated This setting is no longer used.
          */
+        @Deprecated
         public static final String DIM_SCREEN = "dim_screen";
 
         /**
@@ -2334,7 +2336,7 @@
             if (sLockSettings != null && !sIsSystemProcess
                     && MOVED_TO_LOCK_SETTINGS.contains(name)) {
                 try {
-                    return sLockSettings.getString(name, "0", UserId.getCallingUserId());
+                    return sLockSettings.getString(name, "0", UserHandle.getCallingUserId());
                 } catch (RemoteException re) {
                     // Fall through
                 }
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index a1f6735..df85b2f 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -18,6 +18,9 @@
 
 import com.android.internal.content.PackageMonitor;
 
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
 import android.app.ISearchManager;
 import android.app.SearchManager;
 import android.app.SearchableInfo;
@@ -27,14 +30,18 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.database.ContentObserver;
 import android.os.Binder;
 import android.os.Process;
-import android.os.UserId;
+import android.os.RemoteException;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.util.Log;
+import android.util.Slog;
 import android.util.SparseArray;
 
 import java.util.List;
@@ -179,32 +186,76 @@
             Log.e(TAG, "getSearchableInfo(), activity == null");
             return null;
         }
-        return getSearchables(UserId.getCallingUserId()).getSearchableInfo(launchActivity);
+        return getSearchables(UserHandle.getCallingUserId()).getSearchableInfo(launchActivity);
     }
 
     /**
      * Returns a list of the searchable activities that can be included in global search.
      */
     public List<SearchableInfo> getSearchablesInGlobalSearch() {
-        return getSearchables(UserId.getCallingUserId()).getSearchablesInGlobalSearchList();
+        return getSearchables(UserHandle.getCallingUserId()).getSearchablesInGlobalSearchList();
     }
 
     public List<ResolveInfo> getGlobalSearchActivities() {
-        return getSearchables(UserId.getCallingUserId()).getGlobalSearchActivities();
+        return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivities();
     }
 
     /**
      * Gets the name of the global search activity.
      */
     public ComponentName getGlobalSearchActivity() {
-        return getSearchables(UserId.getCallingUserId()).getGlobalSearchActivity();
+        return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivity();
     }
 
     /**
      * Gets the name of the web search activity.
      */
     public ComponentName getWebSearchActivity() {
-        return getSearchables(UserId.getCallingUserId()).getWebSearchActivity();
+        return getSearchables(UserHandle.getCallingUserId()).getWebSearchActivity();
     }
 
+    @Override
+    public ComponentName getAssistIntent(int userHandle) {
+        try {
+            if (userHandle != UserHandle.getCallingUserId()) {
+                // Requesting a different user, make sure that they have the permission
+                if (ActivityManager.checkComponentPermission(
+                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                        Binder.getCallingUid(), -1, true)
+                        == PackageManager.PERMISSION_GRANTED) {
+                    // Translate to the current user id, if caller wasn't aware
+                    if (userHandle == UserHandle.USER_CURRENT) {
+                        long identity = Binder.clearCallingIdentity();
+                        userHandle = ActivityManagerNative.getDefault().getCurrentUser().id;
+                        Binder.restoreCallingIdentity(identity);
+                    }
+                } else {
+                    String msg = "Permission Denial: "
+                            + "Request to getAssistIntent for " + userHandle
+                            + " but is calling from user " + UserHandle.getCallingUserId()
+                            + "; this requires "
+                            + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+                    Slog.w(TAG, msg);
+                    return null;
+                }
+            }
+            IPackageManager pm = AppGlobals.getPackageManager();
+            Intent assistIntent = new Intent(Intent.ACTION_ASSIST);
+            ResolveInfo info =
+                    pm.resolveIntent(assistIntent,
+                    assistIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                    PackageManager.MATCH_DEFAULT_ONLY, userHandle);
+            if (info != null) {
+                return new ComponentName(
+                        info.activityInfo.applicationInfo.packageName,
+                        info.activityInfo.name);
+            }
+        } catch (RemoteException re) {
+            // Local call
+            Log.e(TAG, "RemoteException in getAssistIntent: " + re);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception in getAssistIntent: " + e);
+        }
+        return null;
+    }
 }
diff --git a/core/java/android/service/dreams/DreamManagerService.java b/core/java/android/service/dreams/DreamManagerService.java
index dd177cb..fc3f501 100644
--- a/core/java/android/service/dreams/DreamManagerService.java
+++ b/core/java/android/service/dreams/DreamManagerService.java
@@ -123,7 +123,9 @@
     // IDreamManager method
     @Override
     public boolean isDreaming() {
-        return mCurrentDream != null;
+        synchronized (mLock) {
+            return mCurrentDreamToken != null;
+        }
     }
 
     public void bindDreamComponentL(ComponentName componentName, boolean test) {
diff --git a/core/java/android/speech/tts/BlockingAudioTrack.java b/core/java/android/speech/tts/BlockingAudioTrack.java
index fcadad7..47e2129 100644
--- a/core/java/android/speech/tts/BlockingAudioTrack.java
+++ b/core/java/android/speech/tts/BlockingAudioTrack.java
@@ -67,12 +67,10 @@
     private int mAudioBufferSize;
     private int mBytesWritten = 0;
 
-    private AudioTrack mAudioTrack;
+    // Need to be seen by stop() which can be called from another thread. mAudioTrack will be
+    // set to null only after waitAndRelease().
+    private volatile AudioTrack mAudioTrack;
     private volatile boolean mStopped;
-    // Locks the initialization / uninitialization of the audio track.
-    // This is required because stop() will throw an illegal state exception
-    // if called before init() or after mAudioTrack.release().
-    private final Object mAudioTrackLock = new Object();
 
     BlockingAudioTrack(int streamType, int sampleRate,
             int audioFormat, int channelCount,
@@ -93,19 +91,21 @@
         mStopped = false;
     }
 
-    public void init() {
+    public boolean init() {
         AudioTrack track = createStreamingAudioTrack();
+        mAudioTrack = track;
 
-        synchronized (mAudioTrackLock) {
-            mAudioTrack = track;
+        if (track == null) {
+            return false;
+        } else {
+            return true;
         }
     }
 
     public void stop() {
-        synchronized (mAudioTrackLock) {
-            if (mAudioTrack != null) {
-                mAudioTrack.stop();
-            }
+        AudioTrack track = mAudioTrack;
+        if (track != null) {
+            track.stop();
         }
         mStopped = true;
     }
@@ -120,6 +120,12 @@
     }
 
     public void waitAndRelease() {
+        AudioTrack track = mAudioTrack;
+        if (track == null) {
+            if (DBG) Log.d(TAG, "Audio track null [duplicate call to waitAndRelease ?]");
+            return;
+        }
+
         // For "small" audio tracks, we have to stop() them to make them mixable,
         // else the audio subsystem will wait indefinitely for us to fill the buffer
         // before rendering the track mixable.
@@ -129,11 +135,11 @@
         if (mBytesWritten < mAudioBufferSize && !mStopped) {
             if (DBG) {
                 Log.d(TAG, "Stopping audio track to flush audio, state was : " +
-                        mAudioTrack.getPlayState() + ",stopped= " + mStopped);
+                        track.getPlayState() + ",stopped= " + mStopped);
             }
 
             mIsShortUtterance = true;
-            mAudioTrack.stop();
+            track.stop();
         }
 
         // Block until the audio track is done only if we haven't stopped yet.
@@ -145,11 +151,9 @@
         // The last call to AudioTrack.write( ) will return only after
         // all data from the audioTrack has been sent to the mixer, so
         // it's safe to release at this point.
-        if (DBG) Log.d(TAG, "Releasing audio track [" + mAudioTrack.hashCode() + "]");
-        synchronized (mAudioTrackLock) {
-            mAudioTrack.release();
-            mAudioTrack = null;
-        }
+        if (DBG) Log.d(TAG, "Releasing audio track [" + track.hashCode() + "]");
+        track.release();
+        mAudioTrack = null;
     }
 
 
diff --git a/core/java/android/speech/tts/FileSynthesisCallback.java b/core/java/android/speech/tts/FileSynthesisCallback.java
index 04c3377..3e33e8e 100644
--- a/core/java/android/speech/tts/FileSynthesisCallback.java
+++ b/core/java/android/speech/tts/FileSynthesisCallback.java
@@ -95,6 +95,22 @@
         }
     }
 
+    /**
+     * Checks whether a given file exists, and deletes it if it does.
+     */
+    private boolean maybeCleanupExistingFile(File file) {
+        if (file.exists()) {
+            Log.v(TAG, "File " + file + " exists, deleting.");
+            if (!file.delete()) {
+                Log.e(TAG, "Failed to delete " + file);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
     @Override
     public int getMaxBufferSize() {
         return MAX_AUDIO_BUFFER_SIZE;
@@ -120,6 +136,11 @@
                 cleanUp();
                 throw new IllegalArgumentException("FileSynthesisRequest.start() called twice");
             }
+
+            if (!maybeCleanupExistingFile(mFileName)) {
+                return TextToSpeech.ERROR;
+            }
+
             mSampleRateInHz = sampleRateInHz;
             mAudioFormat = audioFormat;
             mChannelCount = channelCount;
@@ -166,6 +187,12 @@
     public int done() {
         if (DBG) Log.d(TAG, "FileSynthesisRequest.done()");
         synchronized (mStateLock) {
+            if (mDone) {
+                if (DBG) Log.d(TAG, "Duplicate call to done()");
+                // This preserves existing behaviour. Earlier, if done was called twice
+                // we'd return ERROR because mFile == null and we'd add to logspam.
+                return TextToSpeech.ERROR;
+            }
             if (mStopped) {
                 if (DBG) Log.d(TAG, "Request has been aborted.");
                 return TextToSpeech.ERROR;
diff --git a/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java b/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java
index d299d70..e853c9e 100644
--- a/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java
+++ b/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java
@@ -87,7 +87,10 @@
         dispatcher.dispatchOnStart();
 
 
-        mAudioTrack.init();
+        if (!mAudioTrack.init()) {
+            dispatcher.dispatchOnError();
+            return;
+        }
 
         try {
             byte[] buffer = null;
@@ -242,4 +245,3 @@
         }
     }
 }
-
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 4c1a0afc..d124e68 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -549,7 +549,7 @@
         @Override
         public boolean isValid() {
             if (mText == null) {
-                Log.wtf(TAG, "Got null text");
+                Log.e(TAG, "null synthesis text");
                 return false;
             }
             if (mText.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH) {
@@ -641,14 +641,6 @@
         }
 
         @Override
-        public boolean isValid() {
-            if (!super.isValid()) {
-                return false;
-            }
-            return checkFile(mFile);
-        }
-
-        @Override
         protected AbstractSynthesisCallback createSynthesisCallback() {
             return new FileSynthesisCallback(mFile);
         }
@@ -664,33 +656,6 @@
             }
             return status;
         }
-
-        /**
-         * Checks that the given file can be used for synthesis output.
-         */
-        private boolean checkFile(File file) {
-            try {
-                if (file.exists()) {
-                    Log.v(TAG, "File " + file + " exists, deleting.");
-                    if (!file.delete()) {
-                        Log.e(TAG, "Failed to delete " + file);
-                        return false;
-                    }
-                }
-                if (!file.createNewFile()) {
-                    Log.e(TAG, "Can't create file " + file);
-                    return false;
-                }
-                if (!file.delete()) {
-                    Log.e(TAG, "Failed to delete " + file);
-                    return false;
-                }
-                return true;
-            } catch (IOException e) {
-                Log.e(TAG, "Can't use " + file + " due to exception " + e);
-                return false;
-            }
-        }
     }
 
     private class AudioSpeechItem extends SpeechItem {
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 270624c..83a70f3f 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -1042,9 +1042,14 @@
                                          float avail, TruncateAt where,
                                          boolean preserveLength,
                                          EllipsizeCallback callback) {
+
+        final String ellipsis = (where == TruncateAt.END_SMALL) ?
+                Resources.getSystem().getString(R.string.ellipsis_two_dots) :
+                Resources.getSystem().getString(R.string.ellipsis);
+
         return ellipsize(text, paint, avail, where, preserveLength, callback,
                 TextDirectionHeuristics.FIRSTSTRONG_LTR,
-                (where == TruncateAt.END_SMALL) ? ELLIPSIS_TWO_DOTS : ELLIPSIS_NORMAL);
+                ellipsis);
     }
 
     /**
@@ -1700,9 +1705,4 @@
     private static String[] EMPTY_STRING_ARRAY = new String[]{};
 
     private static final char ZWNBS_CHAR = '\uFEFF';
-
-    private static final String ELLIPSIS_NORMAL = Resources.getSystem().getString(
-            R.string.ellipsis);
-    private static final String ELLIPSIS_TWO_DOTS = Resources.getSystem().getString(
-            R.string.ellipsis_two_dots);
 }
diff --git a/core/java/android/util/FloatMath.java b/core/java/android/util/FloatMath.java
index 1d4eda4..e05169a 100644
--- a/core/java/android/util/FloatMath.java
+++ b/core/java/android/util/FloatMath.java
@@ -80,4 +80,14 @@
      * @return the exponential of value
      */
     public static native float exp(float value);
+
+    /**
+     * Returns {@code sqrt(}<i>{@code x}</i><sup>{@code 2}</sup>{@code +} <i>
+     * {@code y}</i><sup>{@code 2}</sup>{@code )}.
+     *
+     * @param x a float number
+     * @param y a float number
+     * @return the hypotenuse
+     */
+    public static native float hypot(float x, float y);
 }
diff --git a/core/java/android/util/Spline.java b/core/java/android/util/Spline.java
new file mode 100644
index 0000000..ed027eb
--- /dev/null
+++ b/core/java/android/util/Spline.java
@@ -0,0 +1,156 @@
+/*
+ * 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 android.util;
+
+/**
+ * Performs spline interpolation given a set of control points.
+ * @hide
+ */
+public final class Spline {
+    private final float[] mX;
+    private final float[] mY;
+    private final float[] mM;
+
+    private Spline(float[] x, float[] y, float[] m) {
+        mX = x;
+        mY = y;
+        mM = m;
+    }
+
+    /**
+     * Creates a monotone cubic spline from a given set of control points.
+     *
+     * The spline is guaranteed to pass through each control point exactly.
+     * Moreover, assuming the control points are monotonic (Y is non-decreasing or
+     * non-increasing) then the interpolated values will also be monotonic.
+     *
+     * This function uses the Fritsch-Carlson method for computing the spline parameters.
+     * http://en.wikipedia.org/wiki/Monotone_cubic_interpolation
+     *
+     * @param x The X component of the control points, strictly increasing.
+     * @param y The Y component of the control points, monotonic.
+     * @return
+     *
+     * @throws IllegalArgumentException if the X or Y arrays are null, have
+     * different lengths or have fewer than 2 values.
+     * @throws IllegalArgumentException if the control points are not monotonic.
+     */
+    public static Spline createMonotoneCubicSpline(float[] x, float[] y) {
+        if (x == null || y == null || x.length != y.length || x.length < 2) {
+            throw new IllegalArgumentException("There must be at least two control "
+                    + "points and the arrays must be of equal length.");
+        }
+
+        final int n = x.length;
+        float[] d = new float[n - 1]; // could optimize this out
+        float[] m = new float[n];
+
+        // Compute slopes of secant lines between successive points.
+        for (int i = 0; i < n - 1; i++) {
+            float h = x[i + 1] - x[i];
+            if (h <= 0f) {
+                throw new IllegalArgumentException("The control points must all "
+                        + "have strictly increasing X values.");
+            }
+            d[i] = (y[i + 1] - y[i]) / h;
+        }
+
+        // Initialize the tangents as the average of the secants.
+        m[0] = d[0];
+        for (int i = 1; i < n - 1; i++) {
+            m[i] = (d[i - 1] + d[i]) * 0.5f;
+        }
+        m[n - 1] = d[n - 2];
+
+        // Update the tangents to preserve monotonicity.
+        for (int i = 0; i < n - 1; i++) {
+            if (d[i] == 0f) { // successive Y values are equal
+                m[i] = 0f;
+                m[i + 1] = 0f;
+            } else {
+                float a = m[i] / d[i];
+                float b = m[i + 1] / d[i];
+                if (a < 0f || b < 0f) {
+                    throw new IllegalArgumentException("The control points must have "
+                            + "monotonic Y values.");
+                }
+                float h = FloatMath.hypot(a, b);
+                if (h > 9f) {
+                    float t = 3f / h;
+                    m[i] = t * a * d[i];
+                    m[i + 1] = t * b * d[i];
+                }
+            }
+        }
+        return new Spline(x, y, m);
+    }
+
+    /**
+     * Interpolates the value of Y = f(X) for given X.
+     * Clamps X to the domain of the spline.
+     *
+     * @param x The X value.
+     * @return The interpolated Y = f(X) value.
+     */
+    public float interpolate(float x) {
+        // Handle the boundary cases.
+        final int n = mX.length;
+        if (Float.isNaN(x)) {
+            return x;
+        }
+        if (x <= mX[0]) {
+            return mY[0];
+        }
+        if (x >= mX[n - 1]) {
+            return mY[n - 1];
+        }
+
+        // Find the index 'i' of the last point with smaller X.
+        // We know this will be within the spline due to the boundary tests.
+        int i = 0;
+        while (x >= mX[i + 1]) {
+            i += 1;
+            if (x == mX[i]) {
+                return mY[i];
+            }
+        }
+
+        // Perform cubic Hermite spline interpolation.
+        float h = mX[i + 1] - mX[i];
+        float t = (x - mX[i]) / h;
+        return (mY[i] * (1 + 2 * t) + h * mM[i] * t) * (1 - t) * (1 - t)
+                + (mY[i + 1] * (3 - 2 * t) + h * mM[i + 1] * (t - 1)) * t * t;
+    }
+
+    // For debugging.
+    @Override
+    public String toString() {
+        StringBuilder str = new StringBuilder();
+        final int n = mX.length;
+        str.append("[");
+        for (int i = 0; i < n; i++) {
+            if (i != 0) {
+                str.append(", ");
+            }
+            str.append("(").append(mX[i]);
+            str.append(", ").append(mY[i]);
+            str.append(": ").append(mM[i]).append(")");
+        }
+        str.append("]");
+        return str.toString();
+    }
+}
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 7d33ff4a..5a4f322 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -18,6 +18,7 @@
 
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
+import android.os.SystemClock;
 import android.text.format.DateUtils;
 
 import com.android.internal.util.XmlUtils;
@@ -391,6 +392,18 @@
         formatDuration(time-now, pw, 0);
     }
 
+    /** @hide Just for debugging; not internationalized. */
+    public static String formatUptime(long time) {
+        final long diff = time - SystemClock.uptimeMillis();
+        if (diff > 0) {
+            return time + " (in " + diff + " ms)";
+        }
+        if (diff < 0) {
+            return time + " (" + -diff + " ms ago)";
+        }
+        return time + " (now)";
+    }
+
     /**
      * Convert a System.currentTimeMillis() value to a time of day value like
      * that printed in logs. MM-DD HH:MM:SS.MMM
diff --git a/core/java/android/view/ContextThemeWrapper.java b/core/java/android/view/ContextThemeWrapper.java
index 626f385..6c733f9 100644
--- a/core/java/android/view/ContextThemeWrapper.java
+++ b/core/java/android/view/ContextThemeWrapper.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.Build;
 
@@ -30,6 +31,8 @@
     private int mThemeResource;
     private Resources.Theme mTheme;
     private LayoutInflater mInflater;
+    private Configuration mOverrideConfiguration;
+    private Resources mResources;
 
     public ContextThemeWrapper() {
         super(null);
@@ -45,6 +48,41 @@
         super.attachBaseContext(newBase);
         mBase = newBase;
     }
+
+    /**
+     * Call to set an "override configuration" on this context -- this is
+     * a configuration that replies one or more values of the standard
+     * configuration that is applied to the context.  See
+     * {@link Context#createConfigurationContext(Configuration)} for more
+     * information.
+     *
+     * <p>This method can only be called once, and must be called before any
+     * calls to {@link #getResources()} are made.
+     */
+    public void applyOverrideConfiguration(Configuration overrideConfiguration) {
+        if (mResources != null) {
+            throw new IllegalStateException("getResources() has already been called");
+        }
+        if (mOverrideConfiguration != null) {
+            throw new IllegalStateException("Override configuration has already been set");
+        }
+        mOverrideConfiguration = new Configuration(overrideConfiguration);
+    }
+
+    @Override
+    public Resources getResources() {
+        if (mResources != null) {
+            return mResources;
+        }
+        if (mOverrideConfiguration == null) {
+            mResources = super.getResources();
+            return mResources;
+        } else {
+            Context resc = createConfigurationContext(mOverrideConfiguration);
+            mResources = resc.getResources();
+            return mResources;
+        }
+    }
     
     @Override public void setTheme(int resid) {
         mThemeResource = resid;
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 407bae5..7aa3bb4 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -22,7 +22,6 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.IBinder;
-import android.os.LocalPowerManager;
 import android.os.Looper;
 import android.view.animation.Animation;
 
@@ -115,11 +114,11 @@
     public final static int ACTION_PASS_TO_USER = 0x00000001;
 
     /**
-     * This key event should extend the user activity timeout and turn the lights on.
+     * This key event should wake the device.
      * To be returned from {@link #interceptKeyBeforeQueueing}.
      * Do not return this and {@link #ACTION_GO_TO_SLEEP} or {@link #ACTION_PASS_TO_USER}.
      */
-    public final static int ACTION_POKE_USER_ACTIVITY = 0x00000002;
+    public final static int ACTION_WAKE_UP = 0x00000002;
 
     /**
      * This key event should put the device to sleep (and engage keyguard if necessary)
@@ -473,11 +472,9 @@
      * Perform initialization of the policy.
      * 
      * @param context The system context we are running in.
-     * @param powerManager 
      */
     public void init(Context context, IWindowManager windowManager,
-            WindowManagerFuncs windowManagerFuncs,
-            LocalPowerManager powerManager);
+            WindowManagerFuncs windowManagerFuncs);
 
     /**
      * Called by window manager once it has the initial, default native
@@ -1093,31 +1090,6 @@
     public void lockNow();
 
     /**
-     * Check to see if a screensaver should be run instead of powering off the screen on timeout. 
-     * 
-     * @return true if the screensaver should run, false if the screen should turn off.
-     * 
-     * @hide
-     */
-    public boolean isScreenSaverEnabled();
-
-    /**
-     * Start the screensaver (if it is enabled and not yet running).
-     * 
-     * @return Whether the screensaver was successfully started.
-     * 
-     * @hide
-     */
-    public boolean startScreenSaver();
-
-    /**
-     * Stop the screensaver if it is running.
-     * 
-     * @hide
-     */
-    public void stopScreenSaver();
-
-    /**
      * Set the last used input method window state. This state is used to make IME transition
      * smooth.
      * @hide
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 30c713e..2b75d83 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -37,8 +37,8 @@
     /**
      * Gets the singleton CookieManager instance. If this method is used
      * before the application instantiates a {@link WebView} instance,
-     * {@link CookieSyncManager#createInstance(Context)} must be called
-     * first.
+     * {@link CookieSyncManager#createInstance CookieSyncManager.createInstance(Context)}
+     * must be called first.
      *
      * @return the singleton CookieManager instance
      */
diff --git a/core/java/android/webkit/DeviceOrientationService.java b/core/java/android/webkit/DeviceOrientationService.java
index 2e8656c..a4d240d 100755
--- a/core/java/android/webkit/DeviceOrientationService.java
+++ b/core/java/android/webkit/DeviceOrientationService.java
@@ -123,7 +123,7 @@
         // The angles are in radians
         float[] rotationAngles = new float[3];
         SensorManager.getOrientation(deviceRotationMatrix, rotationAngles);
-        double alpha = Math.toDegrees(-rotationAngles[0]) - 90.0;
+        double alpha = Math.toDegrees(-rotationAngles[0]);
         while (alpha < 0.0) { alpha += 360.0; } // [0, 360)
         double beta = Math.toDegrees(-rotationAngles[1]);
         while (beta < -180.0) { beta += 360.0; } // [-180, 180)
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 6e4e82c..a5420bbe 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -164,10 +164,7 @@
  *
  * <p>For obvious security reasons, your application has its own
  * cache, cookie store etc.&mdash;it does not share the Browser
- * application's data. Cookies are managed on a separate thread, so
- * operations like index building don't block the UI
- * thread. Follow the instructions in {@link android.webkit.CookieSyncManager}
- * if you want to use cookies in your application.
+ * application's data.
  * </p>
  *
  * <p>By default, requests by the HTML to open new windows are
@@ -801,11 +798,13 @@
      * #loadDataWithBaseURL(String,String,String,String,String)
      * loadDataWithBaseURL()} with an appropriate base URL.
      * <p>
-     * If the value of the encoding parameter is 'base64', then the data must
-     * be encoded as base64. Otherwise, the data must use ASCII encoding for
+     * The encoding parameter specifies whether the data is base64 or URL
+     * encoded. If the data is base64 encoded, the value of the encoding
+     * parameter must be 'base64'. For all other values of the parameter,
+     * including null, it is assumed that the data uses ASCII encoding for
      * octets inside the range of safe URL characters and use the standard %xx
-     * hex encoding of URLs for octets outside that range. For example,
-     * '#', '%', '\', '?' should be replaced by %23, %25, %27, %3f respectively.
+     * hex encoding of URLs for octets outside that range. For example, '#',
+     * '%', '\', '?' should be replaced by %23, %25, %27, %3f respectively.
      * <p>
      * The 'data' scheme URL formed by this method uses the default US-ASCII
      * charset. If you need need to set a different charset, you should form a
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 5b8764b..8d79492 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -16,6 +16,7 @@
 
 package android.webkit;
 
+import android.accessibilityservice.AccessibilityServiceInfo;
 import android.animation.ObjectAnimator;
 import android.annotation.Widget;
 import android.app.ActivityManager;
@@ -378,28 +379,26 @@
                     imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS;
                 }
             }
+            int action = EditorInfo.IME_ACTION_GO;
             switch (type) {
                 case WebTextView.NORMAL_TEXT_FIELD:
-                    imeOptions |= EditorInfo.IME_ACTION_GO;
                     break;
                 case WebTextView.TEXT_AREA:
                     inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE
                             | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
                             | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
-                    imeOptions |= EditorInfo.IME_ACTION_NONE;
+                    action = EditorInfo.IME_ACTION_NONE;
                     break;
                 case WebTextView.PASSWORD:
                     inputType |= EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD;
-                    imeOptions |= EditorInfo.IME_ACTION_GO;
                     break;
                 case WebTextView.SEARCH:
-                    imeOptions |= EditorInfo.IME_ACTION_SEARCH;
+                    action = EditorInfo.IME_ACTION_SEARCH;
                     break;
                 case WebTextView.EMAIL:
                     // inputType needs to be overwritten because of the different text variation.
                     inputType = InputType.TYPE_CLASS_TEXT
                             | InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
-                    imeOptions |= EditorInfo.IME_ACTION_GO;
                     break;
                 case WebTextView.NUMBER:
                     // inputType needs to be overwritten because of the different class.
@@ -407,23 +406,20 @@
                             | InputType.TYPE_NUMBER_FLAG_SIGNED | InputType.TYPE_NUMBER_FLAG_DECIMAL;
                     // Number and telephone do not have both a Tab key and an
                     // action, so set the action to NEXT
-                    imeOptions |= EditorInfo.IME_ACTION_NEXT;
                     break;
                 case WebTextView.TELEPHONE:
                     // inputType needs to be overwritten because of the different class.
                     inputType = InputType.TYPE_CLASS_PHONE;
-                    imeOptions |= EditorInfo.IME_ACTION_NEXT;
                     break;
                 case WebTextView.URL:
                     // TYPE_TEXT_VARIATION_URI prevents Tab key from showing, so
                     // exclude it for now.
-                    imeOptions |= EditorInfo.IME_ACTION_GO;
                     inputType |= InputType.TYPE_TEXT_VARIATION_URI;
                     break;
                 default:
-                    imeOptions |= EditorInfo.IME_ACTION_GO;
                     break;
             }
+            imeOptions |= action;
             mHint = initData.mLabel;
             mInputType = inputType;
             mImeOptions = imeOptions;
@@ -1743,8 +1739,21 @@
         event.setMaxScrollY(Math.max(convertedContentHeight - adjustedViewHeight, 0));
     }
 
-    private boolean isAccessibilityEnabled() {
-        return AccessibilityManager.getInstance(mContext).isEnabled();
+    private boolean isAccessibilityInjectionEnabled() {
+        final AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
+        if (!manager.isEnabled()) {
+            return false;
+        }
+
+        // Accessibility scripts should be injected only when a speaking service
+        // is enabled. This may need to change later to accommodate Braille.
+        final List<AccessibilityServiceInfo> services = manager.getEnabledAccessibilityServiceList(
+                AccessibilityServiceInfo.FEEDBACK_SPOKEN);
+        if (services.isEmpty()) {
+            return false;
+        }
+
+        return true;
     }
 
     private AccessibilityInjector getAccessibilityInjector() {
@@ -3930,7 +3939,7 @@
 
         // reset the flag since we set to true in if need after
         // loading is see onPageFinished(Url)
-        if (isAccessibilityEnabled()) {
+        if (isAccessibilityInjectionEnabled()) {
             getAccessibilityInjector().onPageStarted(url);
         }
 
@@ -3945,7 +3954,7 @@
     /* package */ void onPageFinished(String url) {
         mZoomManager.onPageFinished(url);
 
-        if (isAccessibilityEnabled()) {
+        if (isAccessibilityInjectionEnabled()) {
             getAccessibilityInjector().onPageFinished(url);
         }
     }
@@ -4986,7 +4995,7 @@
         }
 
         // See if the accessibility injector needs to handle this event.
-        if (isAccessibilityEnabled()
+        if (isAccessibilityInjectionEnabled()
                 && getAccessibilityInjector().handleKeyEventIfNecessary(event)) {
             return true;
         }
@@ -5093,7 +5102,7 @@
         }
 
         // See if the accessibility injector needs to handle this event.
-        if (isAccessibilityEnabled()
+        if (isAccessibilityInjectionEnabled()
                 && getAccessibilityInjector().handleKeyEventIfNecessary(event)) {
             return true;
         }
@@ -5344,7 +5353,7 @@
     public void onAttachedToWindow() {
         if (mWebView.hasWindowFocus()) setActive(true);
 
-        if (isAccessibilityEnabled()) {
+        if (isAccessibilityInjectionEnabled()) {
             getAccessibilityInjector().addAccessibilityApisIfNecessary();
         }
 
@@ -5357,7 +5366,7 @@
         mZoomManager.dismissZoomPicker();
         if (mWebView.hasWindowFocus()) setActive(false);
 
-        if (isAccessibilityEnabled()) {
+        if (isAccessibilityInjectionEnabled()) {
             getAccessibilityInjector().removeAccessibilityApisIfNecessary();
         } else {
             // Ensure the injector is cleared if we're detaching from the window
@@ -7439,7 +7448,7 @@
                     break;
 
                 case SELECTION_STRING_CHANGED:
-                    if (isAccessibilityEnabled()) {
+                    if (isAccessibilityInjectionEnabled()) {
                         getAccessibilityInjector()
                                 .handleSelectionChangedIfNecessary((String) msg.obj);
                     }
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 84fe8ce..e63c57f 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -37,7 +37,7 @@
 import android.os.PatternMatcher;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -133,7 +133,7 @@
         mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList,
                 mLaunchedFromUid);
         int count = mAdapter.getCount();
-        if (mLaunchedFromUid < 0 || UserId.isIsolated(mLaunchedFromUid)) {
+        if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
             // Gulp!
             finish();
             return;
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index b016e99..1e268c4 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -18,16 +18,14 @@
 
 import android.net.Credentials;
 import android.net.LocalSocket;
-import android.os.Build;
 import android.os.Process;
+import android.os.SELinux;
 import android.os.SystemProperties;
 import android.util.Log;
 
 import dalvik.system.PathClassLoader;
 import dalvik.system.Zygote;
 
-import android.os.SELinux;
-
 import java.io.BufferedReader;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -234,9 +232,9 @@
                 ZygoteInit.setCloseOnExec(serverPipeFd, true);
             }
 
-            pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,
-                    parsedArgs.gids, parsedArgs.debugFlags, rlimits,
-                    parsedArgs.seInfo, parsedArgs.niceName);
+            pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
+                    parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
+                    parsedArgs.niceName);
         } catch (IOException ex) {
             logAndPrintError(newStderr, "Exception creating pipe", ex);
         } catch (ErrnoException ex) {
@@ -341,6 +339,9 @@
          */
         int debugFlags;
 
+        /** From --mount-external */
+        int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
+
         /** from --target-sdk-version. */
         int targetSdkVersion;
         boolean targetSdkVersionSpecified;
@@ -526,6 +527,10 @@
                                 "Duplicate arg specified");
                     }
                     niceName = arg.substring(arg.indexOf('=') + 1);
+                } else if (arg.equals("--mount-external-singleuser")) {
+                    mountExternal = Zygote.MOUNT_EXTERNAL_SINGLEUSER;
+                } else if (arg.equals("--mount-external-multiuser")) {
+                    mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
                 } else {
                     break;
                 }
diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.java b/core/java/com/android/internal/statusbar/StatusBarNotification.java
index d443523..cb87ac4 100644
--- a/core/java/com/android/internal/statusbar/StatusBarNotification.java
+++ b/core/java/com/android/internal/statusbar/StatusBarNotification.java
@@ -19,6 +19,7 @@
 import android.app.Notification;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.widget.RemoteViews;
 
 
@@ -132,6 +133,11 @@
         return ((notification.flags & Notification.FLAG_ONGOING_EVENT) == 0)
                 && ((notification.flags & Notification.FLAG_NO_CLEAR) == 0);
     }
+
+    /** Returns a userHandle for the instance of the app that posted this notification. */
+    public int getUserId() {
+        return UserHandle.getUserId(this.uid);
+    }
 }
 
 
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index f77e8f3..4777c16 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -34,7 +34,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.os.storage.IMountService;
 import android.provider.Settings;
 import android.security.KeyStore;
@@ -246,7 +246,7 @@
         if (callingUid == android.os.Process.SYSTEM_UID) {
             return mCurrentUserId;
         } else {
-            return UserId.getUserId(callingUid);
+            return UserHandle.getUserId(callingUid);
         }
     }
 
diff --git a/core/java/com/android/internal/widget/LockSettingsService.java b/core/java/com/android/internal/widget/LockSettingsService.java
index 2fb81ac..350e006 100644
--- a/core/java/com/android/internal/widget/LockSettingsService.java
+++ b/core/java/com/android/internal/widget/LockSettingsService.java
@@ -25,7 +25,7 @@
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.SystemProperties;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.text.TextUtils;
@@ -97,7 +97,7 @@
 
     private static final void checkWritePermission(int userId) {
         final int callingUid = Binder.getCallingUid();
-        if (UserId.getAppId(callingUid) != android.os.Process.SYSTEM_UID) {
+        if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID) {
             throw new SecurityException("uid=" + callingUid
                     + " not authorized to write lock settings");
         }
@@ -105,7 +105,7 @@
 
     private static final void checkPasswordReadPermission(int userId) {
         final int callingUid = Binder.getCallingUid();
-        if (UserId.getAppId(callingUid) != android.os.Process.SYSTEM_UID) {
+        if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID) {
             throw new SecurityException("uid=" + callingUid
                     + " not authorized to read lock password");
         }
@@ -113,8 +113,8 @@
 
     private static final void checkReadPermission(int userId) {
         final int callingUid = Binder.getCallingUid();
-        if (UserId.getAppId(callingUid) != android.os.Process.SYSTEM_UID
-                && UserId.getUserId(callingUid) != userId) {
+        if (UserHandle.getAppId(callingUid) != android.os.Process.SYSTEM_UID
+                && UserHandle.getUserId(callingUid) != userId) {
             throw new SecurityException("uid=" + callingUid
                     + " not authorized to read settings of user " + userId);
         }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index b1423ca..f950d3da7d 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -67,7 +67,6 @@
 	android_os_ParcelFileDescriptor.cpp \
 	android_os_Parcel.cpp \
 	android_os_SELinux.cpp \
-	android_os_StatFs.cpp \
 	android_os_SystemClock.cpp \
 	android_os_SystemProperties.cpp \
 	android_os_Trace.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index c936b0b..0c88297 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -134,7 +134,6 @@
 extern int register_android_os_Parcel(JNIEnv* env);
 extern int register_android_os_ParcelFileDescriptor(JNIEnv *env);
 extern int register_android_os_SELinux(JNIEnv* env);
-extern int register_android_os_StatFs(JNIEnv *env);
 extern int register_android_os_SystemProperties(JNIEnv *env);
 extern int register_android_os_SystemClock(JNIEnv* env);
 extern int register_android_os_Trace(JNIEnv* env);
@@ -1142,7 +1141,6 @@
     REG_JNI(register_android_os_MessageQueue),
     REG_JNI(register_android_os_ParcelFileDescriptor),
     REG_JNI(register_android_os_SELinux),
-    REG_JNI(register_android_os_StatFs),
     REG_JNI(register_android_os_Trace),
     REG_JNI(register_android_os_UEventObserver),
     REG_JNI(register_android_net_LocalSocketImpl),
diff --git a/core/jni/android_os_FileUtils.cpp b/core/jni/android_os_FileUtils.cpp
index 8d65cbc..82bfc36 100644
--- a/core/jni/android_os_FileUtils.cpp
+++ b/core/jni/android_os_FileUtils.cpp
@@ -33,19 +33,6 @@
 
 namespace android {
 
-static jfieldID gFileStatusDevFieldID;
-static jfieldID gFileStatusInoFieldID;
-static jfieldID gFileStatusModeFieldID;
-static jfieldID gFileStatusNlinkFieldID;
-static jfieldID gFileStatusUidFieldID;
-static jfieldID gFileStatusGidFieldID;
-static jfieldID gFileStatusSizeFieldID;
-static jfieldID gFileStatusBlksizeFieldID;
-static jfieldID gFileStatusBlocksFieldID;
-static jfieldID gFileStatusAtimeFieldID;
-static jfieldID gFileStatusMtimeFieldID;
-static jfieldID gFileStatusCtimeFieldID;
-
 jint android_os_FileUtils_setPermissions(JNIEnv* env, jobject clazz,
                                          jstring file, jint mode,
                                          jint uid, jint gid)
@@ -68,39 +55,6 @@
     return chmod(file8.string(), mode) == 0 ? 0 : errno;
 }
 
-jint android_os_FileUtils_getPermissions(JNIEnv* env, jobject clazz,
-                                         jstring file, jintArray outArray)
-{
-    const jchar* str = env->GetStringCritical(file, 0);
-    String8 file8;
-    if (str) {
-        file8 = String8(str, env->GetStringLength(file));
-        env->ReleaseStringCritical(file, str);
-    }
-    if (file8.size() <= 0) {
-        return ENOENT;
-    }
-    struct stat st;
-    if (stat(file8.string(), &st) != 0) {
-        return errno;
-    }
-    jint* array = (jint*)env->GetPrimitiveArrayCritical(outArray, 0);
-    if (array) {
-        int len = env->GetArrayLength(outArray);
-        if (len >= 1) {
-            array[0] = st.st_mode;
-        }
-        if (len >= 2) {
-            array[1] = st.st_uid;
-        }
-        if (len >= 3) {
-            array[2] = st.st_gid;
-        }
-    }
-    env->ReleasePrimitiveArrayCritical(outArray, array, 0);
-    return 0;
-}
-
 jint android_os_FileUtils_setUMask(JNIEnv* env, jobject clazz, jint mask)
 {
     return umask(mask);
@@ -127,63 +81,16 @@
     return result;
 }
 
-jboolean android_os_FileUtils_getFileStatus(JNIEnv* env, jobject clazz, jstring path, jobject fileStatus) {
-    const char* pathStr = env->GetStringUTFChars(path, NULL);
-    jboolean ret = false;
-
-    struct stat s;
-    int res = stat(pathStr, &s);
-    if (res == 0) {
-        ret = true;
-        if (fileStatus != NULL) {
-            env->SetIntField(fileStatus, gFileStatusDevFieldID, s.st_dev);
-            env->SetIntField(fileStatus, gFileStatusInoFieldID, s.st_ino);
-            env->SetIntField(fileStatus, gFileStatusModeFieldID, s.st_mode);
-            env->SetIntField(fileStatus, gFileStatusNlinkFieldID, s.st_nlink);
-            env->SetIntField(fileStatus, gFileStatusUidFieldID, s.st_uid);
-            env->SetIntField(fileStatus, gFileStatusGidFieldID, s.st_gid);
-            env->SetLongField(fileStatus, gFileStatusSizeFieldID, s.st_size);
-            env->SetIntField(fileStatus, gFileStatusBlksizeFieldID, s.st_blksize);
-            env->SetLongField(fileStatus, gFileStatusBlocksFieldID, s.st_blocks);
-            env->SetLongField(fileStatus, gFileStatusAtimeFieldID, s.st_atime);
-            env->SetLongField(fileStatus, gFileStatusMtimeFieldID, s.st_mtime);
-            env->SetLongField(fileStatus, gFileStatusCtimeFieldID, s.st_ctime);
-        }
-    }
-
-    env->ReleaseStringUTFChars(path, pathStr);
-
-    return ret;
-}
-
 static const JNINativeMethod methods[] = {
     {"setPermissions",  "(Ljava/lang/String;III)I", (void*)android_os_FileUtils_setPermissions},
-    {"getPermissions",  "(Ljava/lang/String;[I)I", (void*)android_os_FileUtils_getPermissions},
     {"setUMask",        "(I)I",                    (void*)android_os_FileUtils_setUMask},
     {"getFatVolumeId",  "(Ljava/lang/String;)I", (void*)android_os_FileUtils_getFatVolumeId},
-    {"getFileStatusNative", "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z", (void*)android_os_FileUtils_getFileStatus},
 };
 
 static const char* const kFileUtilsPathName = "android/os/FileUtils";
 
 int register_android_os_FileUtils(JNIEnv* env)
 {
-    jclass fileStatusClass = env->FindClass("android/os/FileUtils$FileStatus");
-    LOG_FATAL_IF(fileStatusClass == NULL, "Unable to find class android.os.FileUtils$FileStatus");
-
-    gFileStatusDevFieldID = env->GetFieldID(fileStatusClass, "dev", "I");
-    gFileStatusInoFieldID = env->GetFieldID(fileStatusClass, "ino", "I");
-    gFileStatusModeFieldID = env->GetFieldID(fileStatusClass, "mode", "I");
-    gFileStatusNlinkFieldID = env->GetFieldID(fileStatusClass, "nlink", "I");
-    gFileStatusUidFieldID = env->GetFieldID(fileStatusClass, "uid", "I");
-    gFileStatusGidFieldID = env->GetFieldID(fileStatusClass, "gid", "I");
-    gFileStatusSizeFieldID = env->GetFieldID(fileStatusClass, "size", "J");
-    gFileStatusBlksizeFieldID = env->GetFieldID(fileStatusClass, "blksize", "I");
-    gFileStatusBlocksFieldID = env->GetFieldID(fileStatusClass, "blocks", "J");
-    gFileStatusAtimeFieldID = env->GetFieldID(fileStatusClass, "atime", "J");
-    gFileStatusMtimeFieldID = env->GetFieldID(fileStatusClass, "mtime", "J");
-    gFileStatusCtimeFieldID = env->GetFieldID(fileStatusClass, "ctime", "J");
-
     return AndroidRuntime::registerNativeMethods(
         env, kFileUtilsPathName,
         methods, NELEM(methods));
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
index 40443ff..e813c38 100644
--- a/core/jni/android_os_SELinux.cpp
+++ b/core/jni/android_os_SELinux.cpp
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 #define LOG_TAG "SELinuxJNI"
 #include <utils/Log.h>
 
@@ -6,6 +22,7 @@
 #include "android_runtime/AndroidRuntime.h"
 #ifdef HAVE_SELINUX
 #include "selinux/selinux.h"
+#include "selinux/android.h"
 #endif
 #include <errno.h>
 
@@ -458,6 +475,27 @@
   }
 
   /*
+   * Function: native_restorecon
+   * Purpose: restore default SELinux security context
+   * Parameters: pathname: the pathname for the file to be relabeled
+   * Returns: boolean: (true) file label successfully restored, (false) otherwise
+   * Exceptions: none
+   */
+  static jboolean native_restorecon(JNIEnv *env, jobject clazz, jstring pathname) {
+#ifdef HAVE_SELINUX
+    if (isSELinuxDisabled)
+      return true;
+
+    const char *file = const_cast<char *>(env->GetStringUTFChars(pathname, NULL));
+    int ret = selinux_android_restorecon(file);
+    env->ReleaseStringUTFChars(pathname, file);
+    return (ret == 0);
+#else
+    return true;
+#endif
+  }
+
+  /*
    * JNI registration.
    */
   static JNINativeMethod method_table[] = {
@@ -472,6 +510,7 @@
     { "getPidContext"            , "(I)Ljava/lang/String;"                        , (void*)getPidCon        },
     { "isSELinuxEnforced"        , "()Z"                                          , (void*)isSELinuxEnforced},
     { "isSELinuxEnabled"         , "()Z"                                          , (void*)isSELinuxEnabled },
+    { "native_restorecon"        , "(Ljava/lang/String;)Z"                        , (void*)native_restorecon},
     { "setBooleanValue"          , "(Ljava/lang/String;Z)Z"                       , (void*)setBooleanValue  },
     { "setFileContext"           , "(Ljava/lang/String;Ljava/lang/String;)Z"      , (void*)setFileCon       },
     { "setFSCreateContext"       , "(Ljava/lang/String;)Z"                        , (void*)setFSCreateCon   },
diff --git a/core/jni/android_os_StatFs.cpp b/core/jni/android_os_StatFs.cpp
deleted file mode 100644
index 79d8fef..0000000
--- a/core/jni/android_os_StatFs.cpp
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright 2007, 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.
- */
-
-#if INCLUDE_SYS_MOUNT_FOR_STATFS
-#include <sys/mount.h>
-#else
-#include <sys/statfs.h>
-#endif
-
-#include <errno.h>
-
-#include "jni.h"
-#include "JNIHelp.h"
-#include "android_runtime/AndroidRuntime.h"
-
-
-namespace android
-{
-
-// ----------------------------------------------------------------------------
-
-struct fields_t {
-    jfieldID    context;
-};
-static fields_t fields;
-
-// ----------------------------------------------------------------------------
-
-static jint
-android_os_StatFs_getBlockSize(JNIEnv *env, jobject thiz)
-{
-    struct statfs *stat = (struct statfs *)env->GetIntField(thiz, fields.context);
-    return stat->f_bsize;
-}
-
-static jint
-android_os_StatFs_getBlockCount(JNIEnv *env, jobject thiz)
-{
-    struct statfs *stat = (struct statfs *)env->GetIntField(thiz, fields.context);
-    return stat->f_blocks;
-}
-
-static jint
-android_os_StatFs_getFreeBlocks(JNIEnv *env, jobject thiz)
-{
-    struct statfs *stat = (struct statfs *)env->GetIntField(thiz, fields.context);
-    return stat->f_bfree;
-}
-
-static jint
-android_os_StatFs_getAvailableBlocks(JNIEnv *env, jobject thiz)
-{
-    struct statfs *stat = (struct statfs *)env->GetIntField(thiz, fields.context);
-    return stat->f_bavail;
-}
-
-static void
-android_os_StatFs_native_restat(JNIEnv *env, jobject thiz, jstring path)
-{
-    if (path == NULL) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
-        return;
-    }
-
-    // get the object handle
-    struct statfs *stat = (struct statfs *)env->GetIntField(thiz, fields.context);
-    if (stat == NULL) {
-        jniThrowException(env, "java/lang/NoSuchFieldException", NULL);
-        return;
-    }
-
-    const char* pathstr = env->GetStringUTFChars(path, NULL);
-    if (pathstr == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
-        return;
-    }
-
-    // note that stat will contain the new file data corresponding to
-    // pathstr
-    if (statfs(pathstr, stat) != 0) {
-        ALOGE("statfs %s failed, errno: %d", pathstr, errno);
-        delete stat;
-        env->SetIntField(thiz, fields.context, 0);
-        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
-    }
-    // Release pathstr
-    env->ReleaseStringUTFChars(path, pathstr);
-}
-
-static void
-android_os_StatFs_native_setup(JNIEnv *env, jobject thiz, jstring path)
-{
-    if (path == NULL) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
-        return;
-    }
-
-    struct statfs* stat = new struct statfs;
-    if (stat == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
-        return;
-    }
-    env->SetIntField(thiz, fields.context, (int)stat);
-    android_os_StatFs_native_restat(env, thiz, path);
-}
-
-static void
-android_os_StatFs_native_finalize(JNIEnv *env, jobject thiz)
-{
-    struct statfs *stat = (struct statfs *)env->GetIntField(thiz, fields.context);
-    if (stat != NULL) {
-        delete stat;
-        env->SetIntField(thiz, fields.context, 0);
-    }
-}
-
-// ----------------------------------------------------------------------------
-
-static JNINativeMethod gMethods[] = {
-    {"getBlockSize",       "()I",                       (void *)android_os_StatFs_getBlockSize},
-    {"getBlockCount",      "()I",                       (void *)android_os_StatFs_getBlockCount},
-    {"getFreeBlocks",      "()I",                       (void *)android_os_StatFs_getFreeBlocks},
-    {"getAvailableBlocks", "()I",                       (void *)android_os_StatFs_getAvailableBlocks},
-    {"native_setup",       "(Ljava/lang/String;)V",     (void *)android_os_StatFs_native_setup},
-    {"native_finalize",    "()V",                       (void *)android_os_StatFs_native_finalize},
-    {"native_restat",      "(Ljava/lang/String;)V",     (void *)android_os_StatFs_native_restat},
-};
-
-
-int register_android_os_StatFs(JNIEnv *env)
-{
-    jclass clazz;
-
-    clazz = env->FindClass("android/os/StatFs");
-    if (clazz == NULL) {
-        ALOGE("Can't find android/os/StatFs");
-        return -1;
-    }
-
-    fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
-    if (fields.context == NULL) {
-        ALOGE("Can't find StatFs.mNativeContext");
-        return -1;
-    }
-
-    return AndroidRuntime::registerNativeMethods(env,
-            "android/os/StatFs", gMethods, NELEM(gMethods));
-}
-
-}   // namespace android
diff --git a/core/jni/android_util_FloatMath.cpp b/core/jni/android_util_FloatMath.cpp
index e30756b..529fbe9 100644
--- a/core/jni/android_util_FloatMath.cpp
+++ b/core/jni/android_util_FloatMath.cpp
@@ -29,6 +29,10 @@
     static float ExpF(JNIEnv* env, jobject clazz, float x) {
         return expf(x);
     }
+
+    static float HypotF(JNIEnv* env, jobject clazz, float x, float y) {
+        return hypotf(x, y);
+    }
 };
 
 static JNINativeMethod gMathUtilsMethods[] = {
@@ -38,6 +42,7 @@
     {"cos", "(F)F", (void*) MathUtilsGlue::CosF},
     {"sqrt", "(F)F", (void*) MathUtilsGlue::SqrtF},
     {"exp", "(F)F", (void*) MathUtilsGlue::ExpF},
+    {"hypot", "(FF)F", (void*) MathUtilsGlue::HypotF},
 };
 
 int register_android_util_FloatMath(JNIEnv* env)
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 195b1ef..89be5cb 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -283,7 +283,7 @@
     <!-- @hide -->
     <permission android:name="android.permission.BIND_DIRECTORY_SEARCH"
         android:permissionGroup="android.permission-group.PERSONAL_INFO"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|system" />
 
     <!-- Allows an application to read the user's call log. -->
     <permission android:name="android.permission.READ_CALL_LOG"
@@ -766,7 +766,6 @@
         android:protectionLevel="dangerous"
         android:label="@string/permlab_getTasks"
         android:description="@string/permdesc_getTasks" />
-
     <!-- Allows an application to call APIs that allow it to do interactions
          across the users on the device, using singleton services and
          user-targeted broadcasts.  This permission is not available to
@@ -786,6 +785,15 @@
         android:label="@string/permlab_interactAcrossUsersFull"
         android:description="@string/permdesc_interactAcrossUsersFull" />
 
+    <!-- Allows an application to call APIs that allow it to query and manage
+         users on the device. This permission is not available to
+         third party applications. -->
+    <permission android:name="android.permission.MANAGE_USERS"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="signature"
+        android:label="@string/permlab_manageUsers"
+        android:description="@string/permdesc_manageUsers" />
+    
     <!-- Allows an application to get full detailed information about
          recently running tasks, with full fidelity to the real state.
          @hide -->
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index e13921f..db862c8 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -156,7 +156,7 @@
     <string name="global_action_power_off" msgid="4471879440839879722">"Sit af"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Foutverslag"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Neem foutverslag"</string>
-    <string name="bugreport_message" msgid="398447048750350456">"Dit sal inligting oor die huidige toestand van jou toestel insamel, om as \'n e-posboodskap te stuur. Dit sal \'n tydjie neem vandat die foutverslag begin is totdat dit reg is om gestuur te word; wees asseblief geduldig."</string>
+    <string name="bugreport_message" msgid="398447048750350456">"Dit sal inligting oor die huidige toestand van jou toestel insamel om as \'n e-posboodskap te stuur. Dit sal \'n tydjie neem vandat die foutverslag begin is totdat dit reg is om gestuur te word; wees asseblief geduldig."</string>
     <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Stilmodus"</string>
     <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Klank is AF"</string>
     <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"Klank is AAN"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index bde732c1..17f4dd1 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -156,7 +156,7 @@
     <string name="global_action_power_off" msgid="4471879440839879722">"إيقاف التشغيل"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"تقرير الأخطاء"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"إعداد تقرير بالأخطاء"</string>
-    <string name="bugreport_message" msgid="398447048750350456">"سيجمع هذا معلومات حول حالة جهازك الحالي لإرسالها كرسالة إلكترونية. سيستغرق وقتًا قليلاً من بدء عرض تقرير بالأخطاء وحتى يكون جاهزًا للإرسال، الرجاء الانتظار."</string>
+    <string name="bugreport_message" msgid="398447048750350456">"سيجمع هذا معلومات حول حالة جهازك الحالي لإرسالها كرسالة إلكترونية، ولكنه سيستغرق وقتًا قليلاً من بدء عرض تقرير بالأخطاء. وحتى يكون جاهزًا للإرسال، الرجاء الانتظار."</string>
     <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"وضع صامت"</string>
     <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"الصوت متوقف"</string>
     <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"الصوت قيد التشغيل"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 596e8f5..f72d625 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -156,7 +156,7 @@
     <string name="global_action_power_off" msgid="4471879440839879722">"Ausschalten"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Fehlerbericht"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Fehlerbericht abrufen"</string>
-    <string name="bugreport_message" msgid="398447048750350456">"Bei diesem Fehlerbericht werden Daten zum aktuellen Status Ihres Geräts erfasst und als E-Mail versandt. Vom Start des Berichts bis zu seinem Versand kann es eine Weile dauern. Bitte haben Sie Geduld."</string>
+    <string name="bugreport_message" msgid="398447048750350456">"Bei diesem Fehlerbericht werden Daten zum aktuellen Status Ihres Geräts erfasst und als E-Mail versandt. Vom Start des Berichts bis zu seinem Versand kann es eine Weile dauern. Bitte haben Sie etwas Geduld."</string>
     <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Lautlos-Modus"</string>
     <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Ton ist AUS."</string>
     <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"Ton ist AN."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index d793c5d..6f99078 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -156,7 +156,7 @@
     <string name="global_action_power_off" msgid="4471879440839879722">"خاموش کردن"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"گزارش اشکال"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"گرفتن گزارش اشکال"</string>
-    <string name="bugreport_message" msgid="398447048750350456">"این گزارش اطلاعات مربوط به وضعیت دستگاه کنونی شما را جمع‌آوری می‌کند تا به صورت یک پیام ایمیل ارسال شود. از زمان شروع گزارش اشکال تا آماده شدن برای ارسال اندکی زمان می‌برد؛ لطفاً صبور باشید."</string>
+    <string name="bugreport_message" msgid="398447048750350456">"این گزارش اطلاعات مربوط به وضعیت دستگاه کنونی شما را جمع‌آوری می‌کند تا به صورت یک پیام ایمیل ارسال شود. از زمان شروع گزارش اشکال تا آماده شدن برای ارسال اندکی زمان می‌برد؛ لطفاً شکیبا باشید."</string>
     <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"حالت ساکت"</string>
     <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"صدا خاموش است"</string>
     <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"صدا روشن است"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 7eab72d..e98000a 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -155,7 +155,7 @@
     <string name="global_action_lock" msgid="2844945191792119712">"Verrouillage de l\'écran"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Éteindre"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Rapport de bug"</string>
-    <string name="bugreport_title" msgid="2667494803742548533">"Établir un rapport de bug"</string>
+    <string name="bugreport_title" msgid="2667494803742548533">"Créer un rapport de bug"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Cela permet de recueillir des informations concernant l\'état actuel de votre appareil. Ces informations sont ensuite envoyées sous forme d\'e-mail. Merci de patienter pendant la préparation du rapport de bug. Cette opération peut prendre quelques instants."</string>
     <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Mode silencieux"</string>
     <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Le son est désactivé."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 093c19d..cf8fa01 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1075,7 +1075,7 @@
     <string name="usb_storage_error_message" product="nosdcard" msgid="3017045217365540658">"USB विशाल संग्रहण के लिए आपके USB संग्रहण का उपयोग करने में समस्‍या है."</string>
     <string name="usb_storage_error_message" product="default" msgid="2876018512716970313">"USB विशाल संग्रहण के लिए आपके SD कार्ड का उपयोग करने में समस्‍या है."</string>
     <string name="usb_storage_notification_title" msgid="8175892554757216525">"USB कनेक्ट किया गया"</string>
-    <string name="usb_storage_notification_message" msgid="939822783828183763">"आपके कंप्‍यूटर में/से फ़ाइल की प्रतिलिपि बनाने के लिए चयन करें."</string>
+    <string name="usb_storage_notification_message" msgid="939822783828183763">"आपके कंप्‍यूटर में/से फ़ाइल की प्रतिलिपि बनाने के लिए चुनें."</string>
     <string name="usb_storage_stop_notification_title" msgid="2336058396663516017">"USB संग्रहण बंद करें"</string>
     <string name="usb_storage_stop_notification_message" msgid="1656852098555623822">"USB संग्रहण बंद करने के लिए स्‍पर्श करें."</string>
     <string name="usb_storage_stop_title" msgid="660129851708775853">"USB संग्रहण उपयोग में है"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 6699698..ef759e0 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -134,7 +134,7 @@
     <string name="me" msgid="6545696007631404292">"Saya"</string>
     <string name="power_dialog" product="tablet" msgid="8545351420865202853">"Opsi tablet"</string>
     <string name="power_dialog" product="default" msgid="1319919075463988638">"Opsi telepon"</string>
-    <string name="silent_mode" msgid="7167703389802618663">"Modus senyap"</string>
+    <string name="silent_mode" msgid="7167703389802618663">"Mode senyap"</string>
     <string name="turn_on_radio" msgid="3912793092339962371">"Hidupkan nirkabel"</string>
     <string name="turn_off_radio" msgid="8198784949987062346">"Matikan nirkabel"</string>
     <string name="screen_lock" msgid="799094655496098153">"Kunci layar"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index c8a08de..5bbebc5 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -154,12 +154,9 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Pilihan telefon"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Kunci skrin"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Matikan kuasa"</string>
-    <!-- no translation found for global_action_bug_report (7934010578922304799) -->
-    <skip />
-    <!-- no translation found for bugreport_title (2667494803742548533) -->
-    <skip />
-    <!-- no translation found for bugreport_message (398447048750350456) -->
-    <skip />
+    <string name="global_action_bug_report" msgid="7934010578922304799">"Laporan pepijat"</string>
+    <string name="bugreport_title" msgid="2667494803742548533">"Ambil laporan pepijat"</string>
+    <string name="bugreport_message" msgid="398447048750350456">"Ini akan mengumpul maklumat tentang keadaan peranti semasa anda untuk dihantarkan sebagai mesej e-mel. Proses ini akan mengambil sedikit masa bermula dari laporan pepijat sehingga siap untuk dihantar; jadi diharap bersabar."</string>
     <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Mod senyap"</string>
     <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Bunyi DIMATIKAN"</string>
     <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"Bunyi DIHIDUPKAN"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index c3524db..9bdfcd8 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -154,12 +154,9 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opções do telefone"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Bloquear tela"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Desligar"</string>
-    <!-- no translation found for global_action_bug_report (7934010578922304799) -->
-    <skip />
-    <!-- no translation found for bugreport_title (2667494803742548533) -->
-    <skip />
-    <!-- no translation found for bugreport_message (398447048750350456) -->
-    <skip />
+    <string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de bugs"</string>
+    <string name="bugreport_title" msgid="2667494803742548533">"Obter relatório de bugs"</string>
+    <string name="bugreport_message" msgid="398447048750350456">"Isto coletará informações sobre o estado atual do dispositivo para enviá-las em uma mensagem de e-mail. Após iniciar o relatório de bugs, será necessário aguardar algum tempo até que esteja pronto para ser enviado."</string>
     <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Modo silencioso"</string>
     <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Som DESATIVADO"</string>
     <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"O som está ATIVADO"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 1deae16..7284dd7 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -156,7 +156,7 @@
     <string name="global_action_power_off" msgid="4471879440839879722">"Opriţi alimentarea"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Raport despre erori"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Executaţi un raport despre erori"</string>
-    <string name="bugreport_message" msgid="398447048750350456">"Acest raport va colecta informaţii despre starea actuală a dispozitivului, pentru a le trimite ca mesaj de e-mail. Va dura un timp din momentul pornirii raportului despre erori şi până când acesta va fi gata pentru a fi trimis, prin urmare vă rugăm să aveţi puţină răbdare."</string>
+    <string name="bugreport_message" msgid="398447048750350456">"Acest raport va colecta informaţii despre starea actuală a dispozitivului, pentru a le trimite într-un e-mail. Aveți răbdare după pornirea raportului despre erori până când va fi gata de trimis."</string>
     <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Mod Silenţios"</string>
     <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Sunetul este DEZACTIVAT"</string>
     <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"Sunetul este ACTIVAT"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 2653005..8ce6f4e 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -220,10 +220,10 @@
     <string name="permdesc_receiveWapPush" msgid="748232190220583385">"Cho phép ứng dụng nhận và xử lý tin nhắn WAP. Quyền này bao gồm khả năng giám sát hoặc xóa tin nhắn được gửi cho bạn mà không hiển thị chúng cho bạn."</string>
     <string name="permlab_getTasks" msgid="6466095396623933906">"truy xuất các ứng dụng đang chạy"</string>
     <string name="permdesc_getTasks" msgid="7454215995847658102">"Cho phép ứng dụng truy xuất thông tin về các công việc đã và đang chạy gần đây. Việc này có thể cho phép ứng dụng phát hiện thông tin về những ứng dụng nào đã được sử dụng trên thiết bị."</string>
-    <string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"tương tác với người dùng"</string>
-    <string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Cho phép ứng dụng thực hiện hành động với những người dùng khác trên thiết bị. Ứng dụng độc hại có thể sử dụng quyền này để vi phạm khả năng bảo vệ giữa người dùng."</string>
-    <string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"cấp phép đầy đủ để tương tác với người dùng"</string>
-    <string name="permdesc_interactAcrossUsersFull" msgid="376841368395502366">"Cho phép tất cả các tương tác có thể xảy ra với người dùng."</string>
+    <string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"tương tác giữa người dùng"</string>
+    <string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Cho phép ứng dụng thực hiện hành động giữa những người dùng khác trên thiết bị. Ứng dụng độc hại có thể sử dụng quyền này để vi phạm khả năng bảo vệ giữa người dùng."</string>
+    <string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"cấp phép đầy đủ để tương tác giữa người dùng"</string>
+    <string name="permdesc_interactAcrossUsersFull" msgid="376841368395502366">"Cho phép tất cả các tương tác giữa người dùng."</string>
     <string name="permlab_getDetailedTasks" msgid="6229468674753529501">"truy xuất chi tiết về các ứng dụng đang chạy"</string>
     <string name="permdesc_getDetailedTasks" msgid="153824741440717599">"Cho phép ứng dụng truy xuất thông tin chi tiết về các tác vụ đã và đang chạy gần đây. Ứng dụng độc hại có thể phát hiện thông tin riêng tư về các ứng dụng khác."</string>
     <string name="permlab_reorderTasks" msgid="2018575526934422779">"sắp xếp lại những ứng dụng đang chạy"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 7f6955d..c271baa 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -154,12 +154,9 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"手机选项"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"屏幕锁定"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"关机"</string>
-    <!-- no translation found for global_action_bug_report (7934010578922304799) -->
-    <skip />
-    <!-- no translation found for bugreport_title (2667494803742548533) -->
-    <skip />
-    <!-- no translation found for bugreport_message (398447048750350456) -->
-    <skip />
+    <string name="global_action_bug_report" msgid="7934010578922304799">"错误报告"</string>
+    <string name="bugreport_title" msgid="2667494803742548533">"提交错误报告"</string>
+    <string name="bugreport_message" msgid="398447048750350456">"这会收集有关当前设备状态的信息,并以电子邮件的形式进行发送。从开始生成错误报告到准备好发送需要一点时间,请耐心等待。"</string>
     <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"静音模式"</string>
     <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"声音已关闭"</string>
     <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"声音已开启"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 36dcf29..20a15d1 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -156,7 +156,7 @@
     <string name="global_action_power_off" msgid="4471879440839879722">"關機"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"錯誤報告"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"取得錯誤報告"</string>
-    <string name="bugreport_message" msgid="398447048750350456">"這會收集您目前裝置狀態的相關資訊,以便透過電子郵件傳送。從開始建立錯誤報告到準備傳送,這段過程可能需要一點時間,敬請耐心等候。"</string>
+    <string name="bugreport_message" msgid="398447048750350456">"這會收集您目前裝置狀態的相關資訊,以便透過電子郵件傳送。從錯誤報告開始建立到準備傳送的這段過程可能需要一點時間,敬請耐心等候。"</string>
     <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"靜音模式"</string>
     <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"音效已關閉"</string>
     <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"音效已開啟"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ee0ff8e..e3c957b 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -533,13 +533,22 @@
     <integer name="config_longPressOnHomeBehavior">2</integer>
 
     <!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
-         The N entries of this array define N + 1 zones as follows:
+         The N entries of this array define N + 1 control points as follows:
 
-         Zone 0:        0 <= LUX < array[0]
-         Zone 1:        array[0] <= LUX < array[1]
+         Point 1:        LUX <= 0 (implicit)
+         Point 2:        0 < level[1] == LUX < level[2]
          ...
-         Zone N:        array[N - 1] <= LUX < array[N]
-         Zone N + 1:    array[N] <= LUX < infinity
+         Point N:        level[N - 1] == LUX < level[N]
+         Point N + 1:    level[N] <= LUX < infinity
+
+         The control points must be strictly increasing.  Each control point
+         corresponds to an entry in the brightness backlight values arrays.
+         For example, if LUX == level[1] (first element of the levels array)
+         then the brightness will be determined by value[1] (first element
+         of the brightness values array).
+
+         Spline interpolation is used to determine the auto-brightness
+         backlight values for LUX levels between these control points.
 
          Must be overridden in platform specific overlays -->
     <integer-array name="config_autoBrightnessLevels">
@@ -552,6 +561,7 @@
     <!-- Array of output values for LCD backlight corresponding to the LUX values
          in the config_autoBrightnessLevels array.  This array should have size one greater
          than the size of the config_autoBrightnessLevels array.
+         The brightness values must be between 0 and 255 and be non-decreasing.
          This must be overridden in platform specific overlays -->
     <integer-array name="config_autoBrightnessLcdBacklightValues">
     </integer-array>
@@ -559,6 +569,7 @@
     <!-- Array of output values for button backlight corresponding to the LUX values
          in the config_autoBrightnessLevels array.  This array should have size one greater
          than the size of the config_autoBrightnessLevels array.
+         The brightness values must be between 0 and 255 and be non-decreasing.
          This must be overridden in platform specific overlays -->
     <integer-array name="config_autoBrightnessButtonBacklightValues">
     </integer-array>
@@ -566,6 +577,7 @@
     <!-- Array of output values for keyboard backlight corresponding to the LUX values
          in the config_autoBrightnessLevels array.  This array should have size one greater
          than the size of the config_autoBrightnessLevels array.
+         The brightness values must be between 0 and 255 and be non-decreasing.
          This must be overridden in platform specific overlays -->
     <integer-array name="config_autoBrightnessKeyboardBacklightValues">
     </integer-array>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 352c409..620a002 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -567,6 +567,11 @@
     <string name="permdesc_interactAcrossUsersFull">Allows all possible interactions across
         users.</string>
 
+    <!--  Title of an application permission, listed so the user can choose whether they want to allow the application to create/remove/query users. [CHAR LIMIT=none] -->
+    <string name="permlab_manageUsers">manage users</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to create/remove/query users. [CHAR LIMIT=NONE] -->
+    <string name="permdesc_manageUsers">Allows apps to manage users on the device, including query, creation and deletion.</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=50] -->
     <string name="permlab_getDetailedTasks">retrieve details of running apps</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] -->
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
index 6630601..216344d 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -500,19 +500,15 @@
         log("Turn screen off");
         PowerManager pm =
             (PowerManager) getSystemService(Context.POWER_SERVICE);
-        pm.goToSleep(SystemClock.uptimeMillis() + 100);
+        pm.goToSleep(SystemClock.uptimeMillis());
     }
 
     // Turn screen on
     public void turnScreenOn() {
         log("Turn screen on");
-        IPowerManager mPowerManagerService = IPowerManager.Stub.asInterface(
-                ServiceManager.getService("power"));;
-        try {
-            mPowerManagerService.userActivityWithForce(SystemClock.uptimeMillis(), false, true);
-        } catch (Exception e) {
-            log(e.toString());
-        }
+        PowerManager pm =
+                (PowerManager) getSystemService(Context.POWER_SERVICE);
+        pm.wakeUp(SystemClock.uptimeMillis());
     }
 
     /**
diff --git a/core/tests/coretests/src/android/content/pm/AppCacheTest.java b/core/tests/coretests/src/android/content/pm/AppCacheTest.java
index 0c31e2d..8d53db9 100755
--- a/core/tests/coretests/src/android/content/pm/AppCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/AppCacheTest.java
@@ -24,7 +24,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StatFs;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.MediumTest;
@@ -719,7 +719,7 @@
     File getDataDir() {
         try {
             ApplicationInfo appInfo = getPm().getApplicationInfo(mContext.getPackageName(), 0,
-                    UserId.myUserId());
+                    UserHandle.myUserId());
             return new File(appInfo.dataDir);
         } catch (RemoteException e) {
             throw new RuntimeException("Pacakge manager dead", e);
@@ -748,7 +748,7 @@
     
     @LargeTest
     public void testClearApplicationUserDataNoObserver() throws Exception {
-        getPm().clearApplicationUserData(mContext.getPackageName(), null, UserId.myUserId());
+        getPm().clearApplicationUserData(mContext.getPackageName(), null, UserHandle.myUserId());
         //sleep for 1 minute
         Thread.sleep(60*1000);
         //confirm files dont exist
diff --git a/core/tests/coretests/src/android/os/BrightnessLimit.java b/core/tests/coretests/src/android/os/BrightnessLimit.java
index 5e9b906..f4a5e09 100644
--- a/core/tests/coretests/src/android/os/BrightnessLimit.java
+++ b/core/tests/coretests/src/android/os/BrightnessLimit.java
@@ -49,7 +49,7 @@
                 ServiceManager.getService("power"));
         if (power != null) {
             try {
-                power.setBacklightBrightness(0);
+                power.setTemporaryScreenBrightnessSettingOverride(0);
             } catch (RemoteException darn) {
                 
             }
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index f12cbe1..4d0b892 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -17,21 +17,13 @@
 package android.os;
 
 import android.content.Context;
-import android.os.FileUtils;
-import android.os.FileUtils.FileStatus;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import java.io.ByteArrayInputStream;
 import java.io.File;
-import java.io.FileWriter;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
-import java.io.IOException;
-
-import junit.framework.Assert;
+import java.io.FileWriter;
 
 public class FileUtilsTest extends AndroidTestCase {
     private static final String TEST_DATA =
@@ -60,60 +52,6 @@
         if (mCopyFile.exists()) mCopyFile.delete();
     }
 
-    @LargeTest
-    public void testGetFileStatus() {
-        final byte[] MAGIC = { 0xB, 0xE, 0x0, 0x5 };
-
-        try {
-            // truncate test file and write MAGIC (4 bytes) to it.
-            FileOutputStream os = new FileOutputStream(mTestFile, false);
-            os.write(MAGIC, 0, 4);
-            os.flush();
-            os.close();
-        } catch (FileNotFoundException e) {
-            Assert.fail("File was removed durning test" + e);
-        } catch (IOException e) {
-            Assert.fail("Unexpected IOException: " + e);
-        }
-        
-        Assert.assertTrue(mTestFile.exists());
-        Assert.assertTrue(FileUtils.getFileStatus(mTestFile.getPath(), null));
-        
-        FileStatus status1 = new FileStatus();
-        FileUtils.getFileStatus(mTestFile.getPath(), status1);
-        
-        Assert.assertEquals(4, status1.size);
-        
-        // Sleep for at least one second so that the modification time will be different.
-        try {
-            Thread.sleep(1000);
-        } catch (InterruptedException e) {
-        }
-
-        try {
-            // append so we don't change the creation time.
-            FileOutputStream os = new FileOutputStream(mTestFile, true);
-            os.write(MAGIC, 0, 4);
-            os.flush();
-            os.close();
-        } catch (FileNotFoundException e) {
-            Assert.fail("File was removed durning test" + e);
-        } catch (IOException e) {
-            Assert.fail("Unexpected IOException: " + e);
-        }
-        
-        FileStatus status2 = new FileStatus();
-        FileUtils.getFileStatus(mTestFile.getPath(), status2);
-        
-        Assert.assertEquals(8, status2.size);
-        Assert.assertTrue(status2.mtime > status1.mtime);
-        
-        mTestFile.delete();
-        
-        Assert.assertFalse(mTestFile.exists());
-        Assert.assertFalse(FileUtils.getFileStatus(mTestFile.getPath(), null));
-    }
-
     // TODO: test setPermissions(), getPermissions()
 
     @MediumTest
diff --git a/core/tests/coretests/src/android/os/ProcessTest.java b/core/tests/coretests/src/android/os/ProcessTest.java
new file mode 100644
index 0000000..1f5b7c8
--- /dev/null
+++ b/core/tests/coretests/src/android/os/ProcessTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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 android.os;
+
+import android.os.Process;
+import android.os.UserHandle;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+
+public class ProcessTest extends TestCase {
+
+    @MediumTest
+    public void testProcessGetUidFromName() throws Exception {
+        assertEquals(android.os.Process.SYSTEM_UID, Process.getUidForName("system"));
+        assertEquals(Process.BLUETOOTH_UID, Process.getUidForName("bluetooth"));
+        assertEquals(Process.FIRST_APPLICATION_UID, Process.getUidForName("u0_a0"));
+        assertEquals(UserHandle.getUid(1, Process.SYSTEM_UID), Process.getUidForName("u1_system"));
+        assertEquals(UserHandle.getUid(2, Process.FIRST_ISOLATED_UID),
+                Process.getUidForName("u2_i0"));
+        assertEquals(UserHandle.getUid(3, Process.FIRST_APPLICATION_UID + 100),
+                Process.getUidForName("u3_a100"));
+    }
+
+    @MediumTest
+    public void testProcessGetUidFromNameFailure() throws Exception {
+        // Failure cases
+        assertEquals(-1, Process.getUidForName("u2a_foo"));
+        assertEquals(-1, Process.getUidForName("u1_abcdef"));
+        assertEquals(-1, Process.getUidForName("u23"));
+        assertEquals(-1, Process.getUidForName("u2_i34a"));
+        assertEquals(-1, Process.getUidForName("akjhwiuefhiuhsf"));
+        assertEquals(-1, Process.getUidForName("u5_radio5"));
+        assertEquals(-1, Process.getUidForName("u2jhsajhfkjhsafkhskafhkashfkjashfkjhaskjfdhakj3"));
+    }
+
+}
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
index 71a9a15..134ac0c 100644
--- a/data/etc/Android.mk
+++ b/data/etc/Android.mk
@@ -21,8 +21,6 @@
 
 LOCAL_MODULE := platform.xml
 
-LOCAL_MODULE_TAGS := user
-
 LOCAL_MODULE_CLASS := ETC
 
 # This will install the file in /system/etc/permissions
@@ -38,8 +36,6 @@
 
 #LOCAL_MODULE := required_hardware.xml
 
-#LOCAL_MODULE_TAGS := user
-
 #LOCAL_MODULE_CLASS := ETC
 
 # This will install the file in /system/etc/permissions
diff --git a/docs/downloads/design/Android_Design_Downloads_20120229.zip b/docs/downloads/design/Android_Design_Downloads_20120229.zip
deleted file mode 100644
index 7bbd85f..0000000
--- a/docs/downloads/design/Android_Design_Downloads_20120229.zip
+++ /dev/null
Binary files differ
diff --git a/docs/downloads/design/Android_Design_Downloads_20120814.zip b/docs/downloads/design/Android_Design_Downloads_20120814.zip
new file mode 100755
index 0000000..102b011
--- /dev/null
+++ b/docs/downloads/design/Android_Design_Downloads_20120814.zip
Binary files differ
diff --git a/docs/downloads/design/Android_Design_Fireworks_Stencil_20120229.png b/docs/downloads/design/Android_Design_Fireworks_Stencil_20120229.png
deleted file mode 100644
index 5136379..0000000
--- a/docs/downloads/design/Android_Design_Fireworks_Stencil_20120229.png
+++ /dev/null
Binary files differ
diff --git a/docs/downloads/design/Android_Design_Fireworks_Stencil_20120814.png b/docs/downloads/design/Android_Design_Fireworks_Stencil_20120814.png
new file mode 100755
index 0000000..9a55143
--- /dev/null
+++ b/docs/downloads/design/Android_Design_Fireworks_Stencil_20120814.png
Binary files differ
diff --git a/docs/downloads/design/Android_Design_Holo_Widgets_20120302.zip b/docs/downloads/design/Android_Design_Holo_Widgets_20120302.zip
deleted file mode 100644
index 90c45c6..0000000
--- a/docs/downloads/design/Android_Design_Holo_Widgets_20120302.zip
+++ /dev/null
Binary files differ
diff --git a/docs/downloads/design/Android_Design_Holo_Widgets_20120814.zip b/docs/downloads/design/Android_Design_Holo_Widgets_20120814.zip
new file mode 100755
index 0000000..295affd
--- /dev/null
+++ b/docs/downloads/design/Android_Design_Holo_Widgets_20120814.zip
Binary files differ
diff --git a/docs/downloads/design/Android_Design_Icons_20120229.zip b/docs/downloads/design/Android_Design_Icons_20120229.zip
deleted file mode 100644
index c2728f3..0000000
--- a/docs/downloads/design/Android_Design_Icons_20120229.zip
+++ /dev/null
Binary files differ
diff --git a/docs/downloads/design/Android_Design_Icons_20120814.zip b/docs/downloads/design/Android_Design_Icons_20120814.zip
new file mode 100755
index 0000000..3471438
--- /dev/null
+++ b/docs/downloads/design/Android_Design_Icons_20120814.zip
Binary files differ
diff --git a/docs/downloads/design/Android_Design_Illustrator_Vectors_20120814.ai b/docs/downloads/design/Android_Design_Illustrator_Vectors_20120814.ai
new file mode 100644
index 0000000..928ecfa
--- /dev/null
+++ b/docs/downloads/design/Android_Design_Illustrator_Vectors_20120814.ai
Binary files differ
diff --git a/docs/downloads/design/Android_Design_OmniGraffle_Stencil_20120229.graffle b/docs/downloads/design/Android_Design_OmniGraffle_Stencil_20120229.graffle
deleted file mode 100644
index 9e418d3..0000000
--- a/docs/downloads/design/Android_Design_OmniGraffle_Stencil_20120229.graffle
+++ /dev/null
Binary files differ
diff --git a/docs/downloads/design/Android_Design_OmniGraffle_Stencil_20120814.graffle b/docs/downloads/design/Android_Design_OmniGraffle_Stencil_20120814.graffle
new file mode 100755
index 0000000..d575008
--- /dev/null
+++ b/docs/downloads/design/Android_Design_OmniGraffle_Stencil_20120814.graffle
Binary files differ
diff --git a/docs/html/design/building-blocks/dialogs.jd b/docs/html/design/building-blocks/dialogs.jd
index 9b653ee..728821e 100644
--- a/docs/html/design/building-blocks/dialogs.jd
+++ b/docs/html/design/building-blocks/dialogs.jd
@@ -10,28 +10,29 @@
 <div class="with-callouts">
 
 <ol>
-<li>
-<h4>Optional title region</h4>
-<p>The title introduces the content of your dialog. It can, for example, identify the name of a
- setting that the user is about to change, or request a decision.</p>
-</li>
-<li>
-<h4>Content area</h4>
-<p>Dialog content varies widely. For settings dialogs, a dialog may contain UI elements such as
- sliders, text fields, checkboxes, or radio buttons that allow the user to change app or system
- settings. In other cases, such as alerts, the content may consist solely of text that provides
- further context for a user decision.</p>
-</li>
-<li>
-<h4>Action buttons</h4>
-<p>Action buttons are typically Cancel and/or OK, with OK indicating the preferred or most likely
- action. However, if the options consist of specific actions such as Close or Wait rather than
- a confirmation or cancellation of the action described in the content, then all the buttons
- should be active verbs. As a rule, the dismissive action of a dialog is always on the left
- whereas the affirmative actions are on the right.</p>
-</li>
-</ol>
+  <li>
+  <h4>Optional title region</h4>
+  <p>The title introduces the content of your dialog. It can, for example, identify the name of a
+   setting that the user is about to change, or request a decision.</p>
+  </li>
+  <li>
+  <h4>Content area</h4>
+  <p>Dialog content varies widely. For settings dialogs, a dialog may contain UI elements such as
+   sliders, text fields, checkboxes, or radio buttons that allow the user to change app or system
+   settings. In other cases, such as alerts, the content may consist solely of text that provides
+   further context for a user decision.</p>
+  </li>
 
+  <li>
+  <h4>Action buttons</h4>
+  <p>Action buttons are typically Cancel and/or OK, with OK indicating the preferred or most likely action. However, if the options consist of specific actions such as Close or Wait rather than a confirmation or cancellation of the action described in the content, then all the buttons should be active verbs. Order actions following these rules:</p>
+    <ul>
+    
+    <li>The dismissive action of a dialog is always on the left. Dismissive actions return to the user to the previous state.</li>
+    <li>The affirmative actions are on the right. Affirmative actions continue progress toward the user goal that triggered the dialog.</li>
+    </ul>
+  </li>
+</ol>
 </div>
 
 <img src="{@docRoot}design/media/dialogs_examples.png">
@@ -80,7 +81,46 @@
 
   </div>
 </div>
+<p>When crafting a confirmation dialog, make the title meaningful by echoing the requested action.</p>
 
+<div class="layout-content-row">
+  <div class="layout-content-col span-4">
+    <div class="do-dont-label bad">Don't</div>
+      <table class="ui-table bad">
+      <thead>
+        <tr>
+          <th class="label">
+          Are you sure?
+          </th>
+        </tr>
+      </thead>
+      </table>
+  </div>
+  <div class="layout-content-col span-4">
+    <div class="do-dont-label bad">Don't</div>
+      <table class="ui-table bad">
+      <thead>
+        <tr>
+          <th class="label">
+          Warning!
+          </th>
+        </tr>
+      </thead>
+      </table>
+  </div>
+  <div class="layout-content-col span-5">
+    <div class="do-dont-label good">Do</div>
+      <table class="ui-table good">
+      <thead>
+        <tr>
+          <th class="label">
+          Erase USB storage?
+          </th>
+        </tr>
+      </thead>
+      </table>
+  </div>
+</div>
 
 <h2 id="popups">Popups</h2>
 
diff --git a/docs/html/design/building-blocks/index.jd b/docs/html/design/building-blocks/index.jd
index d915aae..e554775 100644
--- a/docs/html/design/building-blocks/index.jd
+++ b/docs/html/design/building-blocks/index.jd
@@ -11,7 +11,7 @@
 #text-overlay {
   position: absolute;
   left: 0;
-  top: 472px;
+  top: 520px;
   width: 450px;
 }
 </style>
diff --git a/docs/html/design/building-blocks/progress.jd b/docs/html/design/building-blocks/progress.jd
index 03fc09c..7342387 100644
--- a/docs/html/design/building-blocks/progress.jd
+++ b/docs/html/design/building-blocks/progress.jd
@@ -1,19 +1,14 @@
-page.title=Progress and Activity
+page.title=Progress &amp; Activity
 @jd:body
 
-<p>When an operation of interest to the user is taking place over a relatively long period of time,
-provide visual feedback that it's still happening and in the process of being completed.</p>
-<h2 id="progress">Progress</h2>
+<p>Progress bars and activity indicators signal to users that something is happening that will take a moment.</p>
+<h2 id="progress">Progress bars</h2>
 
-<p>If you know the percentage of the operation that has been completed, use a determinate progress bar
-to give the user a sense of how much longer it will take.</p>
+<p>Progress bars are for situations where the percentage completed can be determined. They give users a quick sense of how much longer an operation will take.</p>
 
 <img src="{@docRoot}design/media/progress_download.png">
 
-<p>The progress bar should always travel from 0% to 100% completion. Avoid setting the bar to a lower
-value than a previous value, or using the same progress bar to represent the progress of multiple
-events, since doing so makes the display meaningless. If you're not sure how long a particular
-operation will take, use an indeterminate progress indicator.</p>
+<p>A progress bar should always fill from 0% to 100% and never move backwards to a lower value. If multiple operations are happening in sequence, use the progress bar to represent the delay as a whole, so that when the bar reaches 100%, it doesn't return back to 0%.</p>
 
 <div class="vspace size-2">&nbsp;</div>
 
@@ -22,12 +17,11 @@
   Progress bar in Holo Dark and Holo Light.
 </div>
 
-<h2 id="activity">Activity</h2>
+<h2 id="activity">Activity indicators</h2>
 
-<p>If you don't know how much longer an operation will continue, use an indeterminate progress
-indicator. There are two styles available: a flat bar and a circle. Use the one that best fits the
-available space.</p>
+<p>Activity indicators are for operations of an indeterminate length. They ask users to wait a moment while something finishes up, without getting into specifics about what's happening behind the scenes.</p>
 
+<p>Two styles are available: a bar and a circle. Each is offered in a variety of sizes, in both Holo Light and Holo Dark themes. Choose the appropriate style and size for the surrounding context. For example, the largest activity circle works well when displayed in a blank content area, but not in a smaller dialog box. Each operation should only be represented by one activity indicator.</p>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-6">
@@ -38,14 +32,8 @@
   <div class="layout-content-col span-7 with-callouts">
 
     <ol>
-      <li class="value-1"><h4>Activity bar (shown with the Holo Dark theme)</h4>
-        <p>
-
-An indeterminate activity bar is used at the start of an application download because the Play Store
-app hasn't been able to contact the server yet, and it's not possible to determine how long it will
-take for the download to begin.
-
-        </p>
+      <li class="value-1"><h4>Activity bar</h4>
+        <p>In this example, an activity bar (in Holo Dark) appears when a user first requests a download. There's an unknown period of time when the download has not yet started. As soon as the download starts, this activity bar transforms into a progress bar.</p>
       </li>
     </ol>
 
@@ -61,12 +49,19 @@
   <div class="layout-content-col span-7 with-callouts">
 
     <ol>
-      <li class="value-2"><h4>Activity circle (shown with the Holo Light theme)</h4>
+      <li class="value-2"><h4>Activity circle</h4>
+        <p>In this example, an activity circle (in Holo Light) is used in the Gmail application when a message is being loaded because it's not possible to determine how long it will take to download the email.</p>
+        <p>When displaying an activity circle, do not include text to communicate what the app is doing. The moving circle alone provides sufficient feedback about the delay, and does so in an understated way that minimizes the impact.</p>
         <p>
-
-An indeterminate activity circle is used in the Gmail application when a message is being
-loaded because it's not possible to determine how long it will take to download the email.
-
+        <div class="layout-content-col span-3" style="margin-left:0">
+          <div class="do-dont-label bad">Don't</div>
+          <img src="{@docRoot}design/media/progress_activity_dont.png">
+        </div>
+      
+        <div class="layout-content-col span-3">
+          <div class="do-dont-label good">Do</div>
+          <img src="{@docRoot}design/media/progress_activity_do.png">
+        </div>
         </p>
       </li>
     </ol>
@@ -74,6 +69,34 @@
   </div>
 </div>
 
-<p>You should only use one activity indicator on screen per activity, and it should appropriately sized
-for the surrounding context. For example, the largest activity circle works well when displayed in a
-blank content area, but not in a smaller dialog box.</p>
+<h2 id="custom-indicators">Custom indicators</h2>
+<p>The standard progress bar and activity indicators work well for most situations and should be used whenever possible to provide a consistent experience across Android. However, some situations may call for something more custom.</p>
+
+<p>Here's an example:<br>
+In all of the Google Play apps (Music, Books, Movies, Magazines), we wanted the current download state of each item to be visible at all times at the top-level screen. These states are:
+  <ul>
+    <li>Not downloaded</li>
+    <li>Temporarily downloaded (automatically cached by the app)</li>
+    <li>Permanently downloaded on the device at the user's request</li>
+  </ul>
+</p>
+<p>We also needed to indicate progress from one download state to another, because downloading is not instantaneous.</p>
+<p>This presented a challenge, because the Google Play apps use a variety of different layouts, and some of them are highly space-constrained. We didn't want this information to clutter the top-level screens, or compete too much with the cover art.</p>
+<p>So we designed a custom indicator that could show all of the information in a tiny footprint, with the flexibility to appear on top of content if necessary.</p>
+
+<img src="{@docRoot}design/media/progress_activity_custom.png">
+
+<p>The color indicates whether it's downloaded (blue) or not (gray). The appearance of the pin indicates whether the download is permanent (white, upright) or temporary (gray, diagonal). And when state is in the process of changing, progress is indicated by a moving pie chart.</p>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-9">
+    <img src="{@docRoot}design/media/progress_activity_custom_app.png">
+  </div>
+  <div class="layout-content-col span-4">
+    <div class="figure-caption">
+      Across Google Play apps with different layouts, the same custom indicator appears with each item. It communicates download state as well as progress, in a compact package that can be incorporated into any screen design.
+    </div>
+  </div>
+</div>
+
+<p>If you find that the standard indicators aren't meeting your needs (due to space constraints, state complexities), by all means design your own. Make it feel like part of the Android family by injecting some of the visual characteristics of the standard indicators. In this example, we carried over the circular shape, the same shade of blue, and the flat and simple style.</p>
diff --git a/docs/html/design/building-blocks/tabs.jd b/docs/html/design/building-blocks/tabs.jd
index 19ed1c3..fe05f80 100644
--- a/docs/html/design/building-blocks/tabs.jd
+++ b/docs/html/design/building-blocks/tabs.jd
@@ -6,6 +6,7 @@
 <p>Tabs in the action bar make it easy to explore and switch between different views or functional
 aspects of your app, or to browse categorized data sets.</p>
 
+<p>For details on using gestures to move between tabs, see the <a href="{@docRoot}design/patterns/swipe-views.html">Swipe Views</a> pattern.</p>
 
 <h2 id="scrollable">Scrollable Tabs</h2>
 
@@ -34,9 +35,8 @@
 
 
 <h2 id="fixed">Fixed Tabs</h2>
-
-
-<p>Fixed tabs display all items concurrently. To navigate to a different view, touch the tab.</p>
+<p>Fixed tabs display all items concurrently. To navigate to a different view, touch the tab, or swipe left or right.</p>
+<p>Fixed tabs are displayed with equal width, based on the width of the widest tab label. If there is insufficient room to display all tabs, the tab labels themselves will be scrollable. For this reason, fixed tabs are best suited for displaying 3 or fewer tabs.</p>
 
 <img src="{@docRoot}design/media/tabs_standard.png">
 <div class="figure-caption">
diff --git a/docs/html/design/design_toc.cs b/docs/html/design/design_toc.cs
index a31fdd3..c3020e1 100644
--- a/docs/html/design/design_toc.cs
+++ b/docs/html/design/design_toc.cs
@@ -26,7 +26,7 @@
   <li class="nav-section">
     <div class="nav-section-header"><a href="<?cs var:toroot ?>design/patterns/index.html">Patterns</a></div>
     <ul>
-      <li><a href="<?cs var:toroot ?>design/patterns/new-4-0.html">New in Android 4.0</a></li>
+      <li><a href="<?cs var:toroot ?>design/patterns/new.html">New in Android</a></li>
       <li><a href="<?cs var:toroot ?>design/patterns/gestures.html">Gestures</a></li>
       <li><a href="<?cs var:toroot ?>design/patterns/app-structure.html">App Structure</a></li>
       <li><a href="<?cs var:toroot ?>design/patterns/navigation.html">Navigation</a></li>
@@ -34,9 +34,13 @@
       <li><a href="<?cs var:toroot ?>design/patterns/multi-pane-layouts.html">Multi-pane Layouts</a></li>
       <li><a href="<?cs var:toroot ?>design/patterns/swipe-views.html">Swipe Views</a></li>
       <li><a href="<?cs var:toroot ?>design/patterns/selection.html">Selection</a></li>
+      <li><a href="<?cs var:toroot ?>design/patterns/confirming-acknowledging.html">Confirming &amp; Acknowledging</a></li>
       <li><a href="<?cs var:toroot ?>design/patterns/notifications.html">Notifications</a></li>
+      <li><a href="<?cs var:toroot ?>design/patterns/widgets.html">Widgets</a></li>
       <li><a href="<?cs var:toroot ?>design/patterns/settings.html">Settings</a></li>
+      <li><a href="<?cs var:toroot ?>design/patterns/help.html">Help</a></li>
       <li><a href="<?cs var:toroot ?>design/patterns/compatibility.html">Compatibility</a></li>
+      <li><a href="<?cs var:toroot ?>design/patterns/accessibility.html">Accessibility</a></li>
       <li><a href="<?cs var:toroot ?>design/patterns/pure-android.html">Pure Android</a></li>
     </ul>
   </li>
@@ -63,4 +67,8 @@
     <div class="nav-section-header empty"><a href="<?cs var:toroot ?>design/downloads/index.html">Downloads</a></div>
   </li>
 
+  <li class="nav-section">
+    <div class="nav-section-header empty"><a href="<?cs var:toroot ?>design/videos/index.html">Videos</a></div>
+  </li>
+
 </ul>
\ No newline at end of file
diff --git a/docs/html/design/downloads/index.jd b/docs/html/design/downloads/index.jd
index 67dfd79..4503098 100644
--- a/docs/html/design/downloads/index.jd
+++ b/docs/html/design/downloads/index.jd
@@ -12,7 +12,7 @@
   <div class="layout-content-col span-4">
 
 <p>
-  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Downloads_20120229.zip">Download All</a>
+  <a class="download-button" href="{@docRoot}downloads/design/Android_Design_Downloads_20120814.zip">Download All</a>
 </p>
 
   </div>
@@ -37,9 +37,10 @@
   <div class="layout-content-col span-4">
 
 <p>
-  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Fireworks_Stencil_20120229.png">Adobe&reg; Fireworks&reg; PNG Stencil</a>
-  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_OmniGraffle_Stencil_20120229.graffle">Omni&reg; OmniGraffle&reg; Stencil</a>
-  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Holo_Widgets_20120302.zip">Adobe&reg; Photoshop&reg; Sources</a>
+  <a class="download-button" href="{@docRoot}downloads/design/Android_Design_Fireworks_Stencil_20120814.png">Adobe&reg; Fireworks&reg; PNG Stencil</a>
+  <a class="download-button" href="{@docRoot}downloads/design/Android_Design_Illustrator_Vectors_20120814.ai">Adobe&reg; Illustrator&reg; Stencil</a>
+  <a class="download-button" href="{@docRoot}downloads/design/Android_Design_OmniGraffle_Stencil_20120814.graffle">Omni&reg; OmniGraffle&reg; Stencil</a>
+  <a class="download-button" href="{@docRoot}downloads/design/Android_Design_Holo_Widgets_20120814.zip">Adobe&reg; Photoshop&reg; Sources</a>
 </p>
 
   </div>
@@ -65,7 +66,7 @@
   <div class="layout-content-col span-4">
 
 <p>
-  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Icons_20120229.zip">Action Bar Icon Pack</a>
+  <a class="download-button" href="{@docRoot}downloads/design/Android_Design_Icons_20120814.zip">Action Bar Icon Pack</a>
 </p>
 
   </div>
@@ -90,8 +91,8 @@
   <div class="layout-content-col span-4">
 
 <p>
-  <a class="download-button" href="https://dl-ssl.google.com/android/design/Roboto_Hinted_20111129.zip">Roboto</a>
-  <a class="download-button" href="https://dl-ssl.google.com/android/design/Roboto_Specimen_Book_20111129.pdf">Specimen Book</a>
+  <a class="download-button" href="{@docRoot}downloads/design/Roboto_Hinted_20111129.zip">Roboto</a>
+  <a class="download-button" href="{@docRoot}downloads/design/Roboto_Specimen_Book_20111129.pdf">Specimen Book</a>
 </p>
 
   </div>
@@ -114,7 +115,7 @@
   <div class="layout-content-col span-4">
 
 <p>
-  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Color_Swatches_20120229.zip">Color Swatches</a>
+  <a class="download-button" href="{@docRoot}downloads/design/Android_Design_Color_Swatches_20120229.zip">Color Swatches</a>
 </p>
 
   </div>
diff --git a/docs/html/design/get-started/creative-vision.jd b/docs/html/design/get-started/creative-vision.jd
index 792b97d..c57b185 100644
--- a/docs/html/design/get-started/creative-vision.jd
+++ b/docs/html/design/get-started/creative-vision.jd
@@ -5,13 +5,7 @@
 
 <div class="vspace size-1">&nbsp;</div>
 
-<p>Ice Cream Sandwich (Android 4.0) marks a major milestone for Android design. We touched nearly every
-pixel of the system as we expanded the new design approaches introduced in Honeycomb tablets to all
-types of mobile devices. Starting with the most basic elements, we introduced a new font, Roboto,
-designed for high-resolution displays. Other big changes include framework-level action bars on
-phones and support for new phones without physical buttons.</p>
-<p>We focused the design work with three overarching goals for our core apps and the system at large.
-As you design apps to work with Android, consider these goals:</p>
+<p>We focused the design of Android around three overarching goals, which apply to our core apps as well as the system at large. As you design apps to work with Android, consider these goals:</p>
 
 <div class="vspace size-1">&nbsp;</div>
 
diff --git a/docs/html/design/get-started/ui-overview.jd b/docs/html/design/get-started/ui-overview.jd
index 34cdd06..bfb9ec9 100644
--- a/docs/html/design/get-started/ui-overview.jd
+++ b/docs/html/design/get-started/ui-overview.jd
@@ -101,8 +101,7 @@
 
     <img src="{@docRoot}design/media/notifications_dismiss.png">
 
-<p>Most notifications have a one-line title and a one-line message. The recommended layout for a
-notification includes two lines. If necessary, you can add a third line. Timestamps are optional.</p>
+<p>Notifications can be expanded to uncover more details and relevant actions. When collapsed, notifications have a one-line title and a one-line message.The recommended layout for a notification includes two lines. If necessary, you can add a third line.</p>
 <p>Swiping a notification right or left removes it from the notification drawer.</p>
 
   </div>
diff --git a/docs/html/design/media/accessibility_contentdesc.png b/docs/html/design/media/accessibility_contentdesc.png
new file mode 100644
index 0000000..6515711
--- /dev/null
+++ b/docs/html/design/media/accessibility_contentdesc.png
Binary files differ
diff --git a/docs/html/design/media/action_bar_pattern_share_pack.png b/docs/html/design/media/action_bar_pattern_share_pack.png
index dde18f3..c8cff61 100644
--- a/docs/html/design/media/action_bar_pattern_share_pack.png
+++ b/docs/html/design/media/action_bar_pattern_share_pack.png
Binary files differ
diff --git a/docs/html/design/media/actionbar_drawer.png b/docs/html/design/media/actionbar_drawer.png
new file mode 100644
index 0000000..95e04f5
--- /dev/null
+++ b/docs/html/design/media/actionbar_drawer.png
Binary files differ
diff --git a/docs/html/design/media/app_structure_book_detail_page_flip.png b/docs/html/design/media/app_structure_book_detail_page_flip.png
index 0cca587..1066094 100644
--- a/docs/html/design/media/app_structure_book_detail_page_flip.png
+++ b/docs/html/design/media/app_structure_book_detail_page_flip.png
Binary files differ
diff --git a/docs/html/design/media/app_structure_gallery_filmstrip.png b/docs/html/design/media/app_structure_gallery_filmstrip.png
index 483bafa..a937533 100644
--- a/docs/html/design/media/app_structure_gallery_filmstrip.png
+++ b/docs/html/design/media/app_structure_gallery_filmstrip.png
Binary files differ
diff --git a/docs/html/design/media/building_blocks_landing.png b/docs/html/design/media/building_blocks_landing.png
index 2da47b7..40ab0da 100644
--- a/docs/html/design/media/building_blocks_landing.png
+++ b/docs/html/design/media/building_blocks_landing.png
Binary files differ
diff --git a/docs/html/design/media/color_spectrum.png b/docs/html/design/media/color_spectrum.png
index 7d2c023..b7ab309 100644
--- a/docs/html/design/media/color_spectrum.png
+++ b/docs/html/design/media/color_spectrum.png
Binary files differ
diff --git a/docs/html/design/media/compatibility_physical_buttons.png b/docs/html/design/media/compatibility_physical_buttons.png
index 30d5ddd..66a23c8 100644
--- a/docs/html/design/media/compatibility_physical_buttons.png
+++ b/docs/html/design/media/compatibility_physical_buttons.png
Binary files differ
diff --git a/docs/html/design/media/compatibility_virtual_nav.png b/docs/html/design/media/compatibility_virtual_nav.png
index ea595a4..27d39b2 100644
--- a/docs/html/design/media/compatibility_virtual_nav.png
+++ b/docs/html/design/media/compatibility_virtual_nav.png
Binary files differ
diff --git a/docs/html/design/media/confirm_ack_acknowledge.png b/docs/html/design/media/confirm_ack_acknowledge.png
new file mode 100644
index 0000000..b78eb14
--- /dev/null
+++ b/docs/html/design/media/confirm_ack_acknowledge.png
Binary files differ
diff --git a/docs/html/design/media/confirm_ack_confirming.png b/docs/html/design/media/confirm_ack_confirming.png
new file mode 100644
index 0000000..20a9c02
--- /dev/null
+++ b/docs/html/design/media/confirm_ack_confirming.png
Binary files differ
diff --git a/docs/html/design/media/confirm_ack_draft_deleted.png b/docs/html/design/media/confirm_ack_draft_deleted.png
new file mode 100644
index 0000000..f189db9
--- /dev/null
+++ b/docs/html/design/media/confirm_ack_draft_deleted.png
Binary files differ
diff --git a/docs/html/design/media/confirm_ack_ex_beam.png b/docs/html/design/media/confirm_ack_ex_beam.png
new file mode 100644
index 0000000..d099912
--- /dev/null
+++ b/docs/html/design/media/confirm_ack_ex_beam.png
Binary files differ
diff --git a/docs/html/design/media/confirm_ack_ex_books.png b/docs/html/design/media/confirm_ack_ex_books.png
new file mode 100644
index 0000000..634d7b9
--- /dev/null
+++ b/docs/html/design/media/confirm_ack_ex_books.png
Binary files differ
diff --git a/docs/html/design/media/confirm_ack_ex_draftsave.png b/docs/html/design/media/confirm_ack_ex_draftsave.png
new file mode 100644
index 0000000..473368d8
--- /dev/null
+++ b/docs/html/design/media/confirm_ack_ex_draftsave.png
Binary files differ
diff --git a/docs/html/design/media/confirm_ack_ex_plus1.png b/docs/html/design/media/confirm_ack_ex_plus1.png
new file mode 100644
index 0000000..6de6710
--- /dev/null
+++ b/docs/html/design/media/confirm_ack_ex_plus1.png
Binary files differ
diff --git a/docs/html/design/media/confirm_ack_ex_removeapp.png b/docs/html/design/media/confirm_ack_ex_removeapp.png
new file mode 100644
index 0000000..0abacce
--- /dev/null
+++ b/docs/html/design/media/confirm_ack_ex_removeapp.png
Binary files differ
diff --git a/docs/html/design/media/confirm_ack_flowchart.png b/docs/html/design/media/confirm_ack_flowchart.png
new file mode 100644
index 0000000..3935d47
--- /dev/null
+++ b/docs/html/design/media/confirm_ack_flowchart.png
Binary files differ
diff --git a/docs/html/design/media/dialogs_popups_example.png b/docs/html/design/media/dialogs_popups_example.png
index 2deb00d..c7536f3 100644
--- a/docs/html/design/media/dialogs_popups_example.png
+++ b/docs/html/design/media/dialogs_popups_example.png
Binary files differ
diff --git a/docs/html/design/media/downloads_stencils.png b/docs/html/design/media/downloads_stencils.png
index 9e09319..9b1a9fe 100644
--- a/docs/html/design/media/downloads_stencils.png
+++ b/docs/html/design/media/downloads_stencils.png
Binary files differ
diff --git a/docs/html/design/media/extras_googleio_12.png b/docs/html/design/media/extras_googleio_12.png
new file mode 100644
index 0000000..7ab994d
--- /dev/null
+++ b/docs/html/design/media/extras_googleio_12.png
Binary files differ
diff --git a/docs/html/design/media/help_better.png b/docs/html/design/media/help_better.png
new file mode 100644
index 0000000..83d7b07
--- /dev/null
+++ b/docs/html/design/media/help_better.png
Binary files differ
diff --git a/docs/html/design/media/help_cling.png b/docs/html/design/media/help_cling.png
new file mode 100644
index 0000000..c91d189
--- /dev/null
+++ b/docs/html/design/media/help_cling.png
Binary files differ
diff --git a/docs/html/design/media/help_dont.png b/docs/html/design/media/help_dont.png
new file mode 100644
index 0000000..3c52c97
--- /dev/null
+++ b/docs/html/design/media/help_dont.png
Binary files differ
diff --git a/docs/html/design/media/help_evenbetter.png b/docs/html/design/media/help_evenbetter.png
new file mode 100644
index 0000000..66b9d162
--- /dev/null
+++ b/docs/html/design/media/help_evenbetter.png
Binary files differ
diff --git a/docs/html/design/media/help_overflow.png b/docs/html/design/media/help_overflow.png
new file mode 100644
index 0000000..fb2bc0a
--- /dev/null
+++ b/docs/html/design/media/help_overflow.png
Binary files differ
diff --git a/docs/html/design/media/help_solo_overflow.png b/docs/html/design/media/help_solo_overflow.png
new file mode 100644
index 0000000..9423ede
--- /dev/null
+++ b/docs/html/design/media/help_solo_overflow.png
Binary files differ
diff --git a/docs/html/design/media/iconography_notification_focal.png b/docs/html/design/media/iconography_notification_focal.png
index 20d5e8f..f21954f1 100644
--- a/docs/html/design/media/iconography_notification_focal.png
+++ b/docs/html/design/media/iconography_notification_focal.png
Binary files differ
diff --git a/docs/html/design/media/index_landing_page.png b/docs/html/design/media/index_landing_page.png
index 3f319b0..2065344 100644
--- a/docs/html/design/media/index_landing_page.png
+++ b/docs/html/design/media/index_landing_page.png
Binary files differ
diff --git a/docs/html/design/media/multipane_expand.png b/docs/html/design/media/multipane_expand.png
index f761e5f..6014cc8 100644
--- a/docs/html/design/media/multipane_expand.png
+++ b/docs/html/design/media/multipane_expand.png
Binary files differ
diff --git a/docs/html/design/media/multipane_show.png b/docs/html/design/media/multipane_show.png
index b10c91c..9993c9b 100644
--- a/docs/html/design/media/multipane_show.png
+++ b/docs/html/design/media/multipane_show.png
Binary files differ
diff --git a/docs/html/design/media/new_accessibility.png b/docs/html/design/media/new_accessibility.png
new file mode 100644
index 0000000..864ee5c
--- /dev/null
+++ b/docs/html/design/media/new_accessibility.png
Binary files differ
diff --git a/docs/html/design/media/new_notifications.png b/docs/html/design/media/new_notifications.png
new file mode 100644
index 0000000..a7293c8
--- /dev/null
+++ b/docs/html/design/media/new_notifications.png
Binary files differ
diff --git a/docs/html/design/media/new_widgets.png b/docs/html/design/media/new_widgets.png
new file mode 100644
index 0000000..7e6201b
--- /dev/null
+++ b/docs/html/design/media/new_widgets.png
Binary files differ
diff --git a/docs/html/design/media/notifications_dismiss.png b/docs/html/design/media/notifications_dismiss.png
index 71bed4f..696a97f 100644
--- a/docs/html/design/media/notifications_dismiss.png
+++ b/docs/html/design/media/notifications_dismiss.png
Binary files differ
diff --git a/docs/html/design/media/notifications_expand_contract_msg.png b/docs/html/design/media/notifications_expand_contract_msg.png
new file mode 100644
index 0000000..056c9f2
--- /dev/null
+++ b/docs/html/design/media/notifications_expand_contract_msg.png
Binary files differ
diff --git a/docs/html/design/media/notifications_pattern_additional_fail.png b/docs/html/design/media/notifications_pattern_additional_fail.png
index 707c98c..4f056db 100644
--- a/docs/html/design/media/notifications_pattern_additional_fail.png
+++ b/docs/html/design/media/notifications_pattern_additional_fail.png
Binary files differ
diff --git a/docs/html/design/media/notifications_pattern_additional_win.png b/docs/html/design/media/notifications_pattern_additional_win.png
index eb193d8..9d69dfd 100644
--- a/docs/html/design/media/notifications_pattern_additional_win.png
+++ b/docs/html/design/media/notifications_pattern_additional_win.png
Binary files differ
diff --git a/docs/html/design/media/notifications_pattern_anatomy.png b/docs/html/design/media/notifications_pattern_anatomy.png
index cacc183..c9fdf85 100644
--- a/docs/html/design/media/notifications_pattern_anatomy.png
+++ b/docs/html/design/media/notifications_pattern_anatomy.png
Binary files differ
diff --git a/docs/html/design/media/notifications_pattern_dialog_toast.png b/docs/html/design/media/notifications_pattern_dialog_toast.png
deleted file mode 100644
index 517d57b..0000000
--- a/docs/html/design/media/notifications_pattern_dialog_toast.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/media/notifications_pattern_expand_contract.png b/docs/html/design/media/notifications_pattern_expand_contract.png
new file mode 100644
index 0000000..e89835c
--- /dev/null
+++ b/docs/html/design/media/notifications_pattern_expand_contract.png
Binary files differ
diff --git a/docs/html/design/media/notifications_pattern_expandable.png b/docs/html/design/media/notifications_pattern_expandable.png
new file mode 100644
index 0000000..31cb3f1
--- /dev/null
+++ b/docs/html/design/media/notifications_pattern_expandable.png
Binary files differ
diff --git a/docs/html/design/media/notifications_pattern_ongoing_music.png b/docs/html/design/media/notifications_pattern_ongoing_music.png
index 01039bd..77b24ed 100644
--- a/docs/html/design/media/notifications_pattern_ongoing_music.png
+++ b/docs/html/design/media/notifications_pattern_ongoing_music.png
Binary files differ
diff --git a/docs/html/design/media/notifications_pattern_personal.png b/docs/html/design/media/notifications_pattern_personal.png
new file mode 100644
index 0000000..a7293c8
--- /dev/null
+++ b/docs/html/design/media/notifications_pattern_personal.png
Binary files differ
diff --git a/docs/html/design/media/notifications_pattern_phone_icons.png b/docs/html/design/media/notifications_pattern_phone_icons.png
index 09d8a83..bee66c9 100644
--- a/docs/html/design/media/notifications_pattern_phone_icons.png
+++ b/docs/html/design/media/notifications_pattern_phone_icons.png
Binary files differ
diff --git a/docs/html/design/media/notifications_pattern_priority.png b/docs/html/design/media/notifications_pattern_priority.png
new file mode 100644
index 0000000..e89835c
--- /dev/null
+++ b/docs/html/design/media/notifications_pattern_priority.png
Binary files differ
diff --git a/docs/html/design/media/notifications_pattern_real_time_people.png b/docs/html/design/media/notifications_pattern_real_time_people.png
index 2af40b8..d03b6f0 100644
--- a/docs/html/design/media/notifications_pattern_real_time_people.png
+++ b/docs/html/design/media/notifications_pattern_real_time_people.png
Binary files differ
diff --git a/docs/html/design/media/notifications_pattern_social_fail.png b/docs/html/design/media/notifications_pattern_social_fail.png
index 2c8fddc..aa0e028 100644
--- a/docs/html/design/media/notifications_pattern_social_fail.png
+++ b/docs/html/design/media/notifications_pattern_social_fail.png
Binary files differ
diff --git a/docs/html/design/media/notifications_pattern_two_actions.png b/docs/html/design/media/notifications_pattern_two_actions.png
new file mode 100644
index 0000000..7c19f2e
--- /dev/null
+++ b/docs/html/design/media/notifications_pattern_two_actions.png
Binary files differ
diff --git a/docs/html/design/media/principles_decide_for_me.png b/docs/html/design/media/principles_decide_for_me.png
index 2d8b883..8080b4e 100644
--- a/docs/html/design/media/principles_decide_for_me.png
+++ b/docs/html/design/media/principles_decide_for_me.png
Binary files differ
diff --git a/docs/html/design/media/principles_error.png b/docs/html/design/media/principles_error.png
index 93767660..c867fe6 100644
--- a/docs/html/design/media/principles_error.png
+++ b/docs/html/design/media/principles_error.png
Binary files differ
diff --git a/docs/html/design/media/principles_information_when_need_it.png b/docs/html/design/media/principles_information_when_need_it.png
index c5ef3ca..d78d5c5 100644
--- a/docs/html/design/media/principles_information_when_need_it.png
+++ b/docs/html/design/media/principles_information_when_need_it.png
Binary files differ
diff --git a/docs/html/design/media/principles_keep_it_brief.png b/docs/html/design/media/principles_keep_it_brief.png
index 9c2813b..ee7dbbb 100644
--- a/docs/html/design/media/principles_keep_it_brief.png
+++ b/docs/html/design/media/principles_keep_it_brief.png
Binary files differ
diff --git a/docs/html/design/media/principles_never_lose_stuff.png b/docs/html/design/media/principles_never_lose_stuff.png
index acbefea..84037d9 100644
--- a/docs/html/design/media/principles_never_lose_stuff.png
+++ b/docs/html/design/media/principles_never_lose_stuff.png
Binary files differ
diff --git a/docs/html/design/media/progress_activity_custom.png b/docs/html/design/media/progress_activity_custom.png
new file mode 100644
index 0000000..2bfdd52
--- /dev/null
+++ b/docs/html/design/media/progress_activity_custom.png
Binary files differ
diff --git a/docs/html/design/media/progress_activity_custom_app.png b/docs/html/design/media/progress_activity_custom_app.png
new file mode 100644
index 0000000..e572508
--- /dev/null
+++ b/docs/html/design/media/progress_activity_custom_app.png
Binary files differ
diff --git a/docs/html/design/media/progress_activity_do.png b/docs/html/design/media/progress_activity_do.png
new file mode 100644
index 0000000..fd22436
--- /dev/null
+++ b/docs/html/design/media/progress_activity_do.png
Binary files differ
diff --git a/docs/html/design/media/progress_activity_dont.png b/docs/html/design/media/progress_activity_dont.png
new file mode 100644
index 0000000..08c4b5d
--- /dev/null
+++ b/docs/html/design/media/progress_activity_dont.png
Binary files differ
diff --git a/docs/html/design/media/swipe_views2.png b/docs/html/design/media/swipe_views2.png
index 6479a2f..ee0f2c4 100644
--- a/docs/html/design/media/swipe_views2.png
+++ b/docs/html/design/media/swipe_views2.png
Binary files differ
diff --git a/docs/html/design/media/swipe_views3.png b/docs/html/design/media/swipe_views3.png
new file mode 100644
index 0000000..bdf9994
--- /dev/null
+++ b/docs/html/design/media/swipe_views3.png
Binary files differ
diff --git a/docs/html/design/media/tabs_youtube.png b/docs/html/design/media/tabs_youtube.png
index 69e9268..4ea6c1c 100644
--- a/docs/html/design/media/tabs_youtube.png
+++ b/docs/html/design/media/tabs_youtube.png
Binary files differ
diff --git a/docs/html/design/media/themes_holo_dark.png b/docs/html/design/media/themes_holo_dark.png
index 0a5876a..e1f4477 100644
--- a/docs/html/design/media/themes_holo_dark.png
+++ b/docs/html/design/media/themes_holo_dark.png
Binary files differ
diff --git a/docs/html/design/media/themes_holo_inverse.png b/docs/html/design/media/themes_holo_inverse.png
index 50be4fb..528d119 100644
--- a/docs/html/design/media/themes_holo_inverse.png
+++ b/docs/html/design/media/themes_holo_inverse.png
Binary files differ
diff --git a/docs/html/design/media/themes_holo_light.png b/docs/html/design/media/themes_holo_light.png
index edc7f77..4f34bb3 100644
--- a/docs/html/design/media/themes_holo_light.png
+++ b/docs/html/design/media/themes_holo_light.png
Binary files differ
diff --git a/docs/html/design/media/ui_overview_notifications.png b/docs/html/design/media/ui_overview_notifications.png
index bc0513f..fe4375e 100644
--- a/docs/html/design/media/ui_overview_notifications.png
+++ b/docs/html/design/media/ui_overview_notifications.png
Binary files differ
diff --git a/docs/html/design/media/widgets_collection_bookmarks.png b/docs/html/design/media/widgets_collection_bookmarks.png
new file mode 100644
index 0000000..86d4d88
--- /dev/null
+++ b/docs/html/design/media/widgets_collection_bookmarks.png
Binary files differ
diff --git a/docs/html/design/media/widgets_collection_gmail.png b/docs/html/design/media/widgets_collection_gmail.png
new file mode 100644
index 0000000..bbd538d
--- /dev/null
+++ b/docs/html/design/media/widgets_collection_gmail.png
Binary files differ
diff --git a/docs/html/design/media/widgets_config.png b/docs/html/design/media/widgets_config.png
new file mode 100644
index 0000000..0ac3473
--- /dev/null
+++ b/docs/html/design/media/widgets_config.png
Binary files differ
diff --git a/docs/html/design/media/widgets_control.png b/docs/html/design/media/widgets_control.png
new file mode 100644
index 0000000..a46add8
--- /dev/null
+++ b/docs/html/design/media/widgets_control.png
Binary files differ
diff --git a/docs/html/design/media/widgets_gestures.png b/docs/html/design/media/widgets_gestures.png
new file mode 100644
index 0000000..f991609
--- /dev/null
+++ b/docs/html/design/media/widgets_gestures.png
Binary files differ
diff --git a/docs/html/design/media/widgets_hybrid.png b/docs/html/design/media/widgets_hybrid.png
new file mode 100644
index 0000000..470f75f
--- /dev/null
+++ b/docs/html/design/media/widgets_hybrid.png
Binary files differ
diff --git a/docs/html/design/media/widgets_info.png b/docs/html/design/media/widgets_info.png
new file mode 100644
index 0000000..6621158
--- /dev/null
+++ b/docs/html/design/media/widgets_info.png
Binary files differ
diff --git a/docs/html/design/media/widgets_resizing01.png b/docs/html/design/media/widgets_resizing01.png
new file mode 100644
index 0000000..5c85df6
--- /dev/null
+++ b/docs/html/design/media/widgets_resizing01.png
Binary files differ
diff --git a/docs/html/design/media/widgets_resizing02.png b/docs/html/design/media/widgets_resizing02.png
new file mode 100644
index 0000000..28f9461
--- /dev/null
+++ b/docs/html/design/media/widgets_resizing02.png
Binary files differ
diff --git a/docs/html/design/patterns/accessibility.jd b/docs/html/design/patterns/accessibility.jd
new file mode 100644
index 0000000..b2fbda9
--- /dev/null
+++ b/docs/html/design/patterns/accessibility.jd
@@ -0,0 +1,80 @@
+page.title=Accessibility
+@jd:body
+
+<p>One of Android's missions is to organize the world's information and make it universally accessible and useful. Accessibility is the measure of how successfully a product can be used by people with varying abilities. Our mission applies to all users-including people with disabilities such as visual impairment, color deficiency, hearing loss, and limited dexterity.</p>
+<p><a href="https://www.google.com/#hl=en&q=universal+design&fp=1">Universal design</a> is the practice of making products that are inherently accessible to all users, regardless of ability. The Android design patterns were created in accordance with universal design principles, and following them will help your app meet basic usability standards. Adhering to universal design and enabling Android's accessibility tools will make your app as accessible as possible.</p>
+<p>Robust support for accessibility will increase your app's user base. It may also be required for adoption by some organizations.</p>
+<p><a href="http://www.google.com/accessibility/">Learn more about Google and accessibility.</a></p>
+
+<h2 id="tools">Android's Accessibility Tools</h2>
+<p>Android includes several features that support access for users with visual impairments; they don't require drastic visual changes to your app.</p>
+
+<ul>
+  <li><strong><a href="https://play.google.com/store/apps/details?id=com.google.android.marvin.talkback">TalkBack</a></strong> is a pre-installed screen reader service provided by Google. It uses spoken feedback to describe the results of actions such as launching an app, and events such as notifications.</li>
+  <li><strong>Explore by Touch</strong> is a system feature that works with TalkBack, allowing you to touch your device's screen and hear what's under your finger via spoken feedback. This feature is helpful to users with low vision.</li>
+  <li><strong>Accessibility settings</strong> let you modify your device's display and sound options, such as increasing the text size, changing the speed at which text is spoken, and more.</li>
+</ul>
+
+<p>Some users use hardware or software directional controllers (such as a D-pad, trackball, keyboard) to jump from selection to selection on a screen. They interact with the structure of your app in a linear fashion, similar to 4-way remote control navigation on a television.</p>
+
+<h2 id="tools">Guidelines</h2>
+<p>The Android design principle "I should always know where I am" is key for accessibility concerns. As a user navigates through an application, they need feedback and a mental model of where they are. All users benefit from a strong sense of information hierarchy and an architecture that makes sense. Most users benefit from visual and haptic feedback during their navigation (such as labels, colors, icons, touch feedback) Low vision users benefit from explicit verbal descriptions and large visuals with high contrast.</p>
+<p>As you design your app, think about the labels and notations needed to navigate your app by sound. When using Explore by Touch, the user enables an invisible but audible layer of structure in your application. Like any other aspect of app design, this structure can be simple, elegant, and robust. The following are Android's recommended guidelines to enable effective navigation for all users.</p>
+
+<h4>Make navigation intuitive</h4>
+<p>Design well-defined, clear task flows with minimal navigation steps, especially for major user tasks. Make sure those tasks are navigable via focus controls. </p>
+
+<h4>Use recommended touch target sizes</h4>
+<p>48 dp is the recommended touch target size for on screen elements. Read about <a href="{@docRoot}design/style/metrics-grids.html">Android Metrics and Grids</a> to learn about implementation strategies to help most of your users. For certain users, it may be appropriate to use larger touch targets. An example of this is educational apps, where buttons larger than the minimum recommendations are appropriate for children with developing motor skills and people with manual dexterity challenges.</p>
+
+
+<h4>Label visual UI elements meaningfully</h4>
+<p>In your wireframes, <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#label-ui">label functional UI components</a> that have no visible text. Those components might be buttons, icons, tabs with icons, and icons with state (like stars). Developers can use the <code><a href="{@docRoot}guide/topics/ui/accessibility/apps.html#label-ui">contentDescription</a></code> attribute to set the label.</p>
+
+<div class ="layout-content-row">
+    <div class="layout-content-col span-8">
+      <img src="{@docRoot}design/media/accessibility_contentdesc.png">
+    </div>
+    <div class="layout-content-col span-5 with-callouts">
+      <ol>
+        <li class="value-1">group</li>
+        <li class="value-2">all contacts</li>
+        <li class="value-3">favorites</li>
+        <li class="value-4">search</li>
+        <li class="value-5">action overflow button</li>
+        <li class="value-6">
+          <em>when starred:</em> remove from favorites </br>
+          <em>when not starred:</em> add to favorties</li>
+        <li class="value-7">action overflow button</li>
+        <li class="value-8">text message</li>
+        <li class="value-9">video chat</li>
+      </ol>
+  </div>
+</div>
+
+<h4>Provide alternatives to affordances that time out</h4>
+<p>Your app may have icons or controls that disappear after a certain amount of time. For example, five seconds after starting a video, playback controls may fade from the screen.</p>
+
+<p>Due to the way that TalkBack works, those controls are not read out loud unless they are focused on. If they fade out from the screen quickly, your user may not even be aware that they are available. Therefore, make sure that you are not relying on timed out controls for high priority task flows. (This is a good universal design guideline too.) If the controls enable an important function, make sure that the user can turn on the controls again and/or their function is duplicated elsewhere. You can also change the behavior of your app when accessibility services are turned on. Your developer may be able to make sure that timed-out controls won't disappear.</p>
+
+<h4>Use standard framework controls or enable TalkBack for custom controls</h4>
+<p>Standard Android framework controls work automatically with accessibility services and have ContentDescriptions built in by default.</p>
+
+<p>An oft-overlooked system control is font size. Users can turn on a system-wide large font size in Settings; using the default system font size in your application will enable the user's preferences in your app as well. To enable system font size in your app, mark text and their associated containers to be measured in <a href="{@docRoot}guide/practices/screens_support.html#screen-independence">scale pixels</a>.</p>
+
+<p>Also, keep in mind that when users have large fonts enabled or speak a different language than you, their type might be larger than the space you've allotted for it. Read <a href="{@docRoot}design/style/devices-displays.html">Devices and Displays</a> and <a href="http://developer.android.com/guide/practices/screens_support.html">Supporting Multiple Screens</a> for design strategies.</p>
+
+<p>If you use custom controls, Android has the developer tools in place to allow adherence to the above guidelines and provide meaningful descriptions about the UI. Provide adequate notation on your wireframes and direct your developer to the <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#custom-views">Custom Views</a> documentation.</p>
+
+<h4>Try it out yourself</h4>
+<p>Turn on the TalkBack service in <strong>Settings > Accessibility</strong> and navigate your application using directional controls or eyes-free navigation.</p>
+
+<h2>Checklist</h2>
+<ul>
+  <li>Make navigation intuitive</li>
+  <li>Use recommended touch target sizes</li>
+  <li>Label visual UI elements meaningfully</li>
+  <li>Provide alternatives to affordances that time out</li>
+  <li>Use standard framework controls or enable TalkBack for custom controls</li>
+  <li>Try it out yourself</li>
+</ul>
\ No newline at end of file
diff --git a/docs/html/design/patterns/actionbar.jd b/docs/html/design/patterns/actionbar.jd
index 4206301..ba5b400 100644
--- a/docs/html/design/patterns/actionbar.jd
+++ b/docs/html/design/patterns/actionbar.jd
@@ -3,20 +3,15 @@
 
 <img src="{@docRoot}design/media/action_bar_pattern_overview.png">
 
-<p>The <em>action bar</em> is arguably the most important structural element of an Android app. It's a
-dedicated piece of real estate at the top of each screen that is generally persistent throughout the
-app.</p>
-<p><strong>The main purpose of the action bar is to</strong>:</p>
+<p>The <em>action bar</em> is a dedicated piece of real estate at the top of each screen that is generally persistent throughout the app.</p>
+<p><strong>It provides several key functions</strong>:</p>
 <ul>
-<li>Make important actions (such as <em>New</em> or <em>Search</em>, etc) prominent and accessible in a predictable
-   way.</li>
-<li>Support consistent navigation and view switching within apps.</li>
-<li>Reduce clutter by providing an action overflow for rarely used actions.</li>
-<li>Provide a dedicated space for giving your app an identity.</li>
+  <li>Makes important actions prominent and accessible in a predictable way (such as <em>New</em> or <em>Search</em>).</li>
+  <li>Supports consistent navigation and view switching within apps.</li>
+  <li>Reduces clutter by providing an action overflow for rarely used actions.</li>
+  <li>Provides a dedicated space for giving your app an identity.</li>
 </ul>
-<p>If you're new to writing Android apps, note that the action bar is one of the most important design
-elements you can implement. Following the guidelines described here will go a long way toward making
-your app's interface consistent with the core Android apps.</p>
+<p>If you're new to writing Android apps, note that the action bar is one of the most important design elements you can implement. Following the guidelines described here will go a long way toward making your app's interface consistent with the core Android apps.</p>
 <h2 id="organization">General Organization</h2>
 
 <p>The action bar is split into four different functional areas that apply to most apps.</p>
@@ -66,7 +61,7 @@
         <p>
 
 Show the most important actions of your app in the actions section. Actions that don't fit in the
-action bar are moved automatically to the action overflow.
+action bar are moved automatically to the action overflow. Long-press on an icon to view the action's name.
 
         </p>
       </li>
@@ -144,28 +139,44 @@
 <p>For more information, refer to the <a href="{@docRoot}design/patterns/selection.html">Selection
 pattern</a>.</p>
 
-<h2 id="elements">Action Bar Elements</h2>
+<h2 id="elements">View Controls</h2>
+<p>If your app displays data in different views, the action bar has three different controls to allow users to switch between them: tabs, spinners, and drawers.</p>
 
 <h4>Tabs</h4>
-<p><em>Tabs</em> display app views concurrently and make it easy to explore and switch between them. Use tabs
-if you expect your users to switch views frequently.</p>
+<p><em>Tabs</em> display app views concurrently and make it easy to explore and switch between them. Tabs may be fixed, where all tabs are simultaneously displayed, or may scroll, allowing a larger number of views to be presented.</p>
 
 <img src="{@docRoot}design/media/tabs_youtube.png">
 
-<p>There are two types of tabs: fixed and scrollable.</p>
+<p><strong>Use tabs if</strong>:</p>
+<ul>
+<li>You expect your app's users to switch views frequently.</li>
+<li>You want the user to be highly aware of the alternate views.</li>
+</ul>
 
+<h4>Fixed tabs</h4>
 <div class="layout-content-row">
   <div class="layout-content-col span-6">
+<p><em>Fixed tabs</em> are always visible on the screen, and can't be moved out of the way like scrollable
+tabs. Fixed tabs in the main action bar can move to the top bar when the screen orientation changes.</p>
+
+<p>Use fixed tabs to support quick changes between two or three app views. Fixed tabs should always allow the user to navigate between the views by swiping left or right on the content area.</p>
+
+  </div>
+  <div class="layout-content-col span-7">
+
+    <img src="{@docRoot}design/media/action_bar_pattern_default_tabs.png">
+    <div class="figure-caption">
+      Default fixed tabs shown in Holo Dark &amp; Light.
+    </div>
+
+  </div>
+</div>
 
 <h4>Scrollable tabs</h4>
-<p><em>Scrollable tabs</em> always take up the entire width of the bar, with the currently active view item in
-the center, and therefore need to live in a dedicated bar. Scrollable tabs can themselves be
-scrolled horizontally to bring more tabs into view.</p>
-<p>Use scrollable tabs if you have a large number of views or if you're unsure how many views will be
-displayed because your app inserts views dynamically (for example, open chats in a messaging app
-that the user can navigate between). Scrollable tabs should always allow the user to navigate
-between the views by swiping left or right on the content area as well as swiping the tabs
-themselves.</p>
+<div class="layout-content-row">
+  <div class="layout-content-col span-6">
+<p><em>Scrollable tabs</em> always take up the entire width of the bar, with the currently active view item in the center, and therefore need to live in a dedicated bar. Scrollable tabs can themselves be scrolled horizontally to bring more tabs into view.</p>
+<p>Use scrollable tabs if you have a large number of views or if you're unsure how many views will be displayed because your app inserts views dynamically (for example, open chats in a messaging app that the user can navigate between). Scrollable tabs should always allow the user to navigate between the views by swiping left or right on the content area as well as swiping the tabs themselves.</p>
 
   </div>
   <div class="layout-content-col span-7">
@@ -186,30 +197,12 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-6">
 
-<h4>Fixed tabs</h4>
-<p><em>Fixed tabs</em> are always visible on the screen, and can't be moved out of the way like scrollable
-tabs. Fixed tabs in the main action bar can move to the top bar when the screen orientation changes.</p>
-
-  </div>
-  <div class="layout-content-col span-7">
-
-    <img src="{@docRoot}design/media/action_bar_pattern_default_tabs.png">
-    <div class="figure-caption">
-      Default fixed tabs shown in Holo Dark &amp; Light.
-    </div>
-
-  </div>
-</div>
-
-<div class="layout-content-row">
-  <div class="layout-content-col span-6">
-
 <h4>Spinners</h4>
 <p>A <em>spinner</em> is a drop-down menu that allows users to switch between views of your app. </p>
-<p><strong>Use spinners rather than tabs in the main action bar if</strong>:</p>
+<p><strong>Use a spinner in the main action bar if</strong>:</p>
 <ul>
 <li>You don't want to give up the vertical screen real estate for a dedicated tab bar.</li>
-<li>You expect your app's users to switch views infrequently.</li>
+<li>The user is switching between views of the same data set (for example: calendar events viewed by day, week, or month) or data sets of the same type (such as content for two different accounts).</li>
 </ul>
 
   </div>
@@ -223,7 +216,24 @@
   </div>
 </div>
 
-<h4>Action buttons</h4>
+<h4>Drawers</h4>
+<div class="layout-content-row">
+  <div class="layout-content-col span-6">
+<p>A <em>drawer</em> is a slide-out menu that allows users to switch between views of your app. It can be opened by touching the action bar's app icon (decorated with the Up caret.) Additionally, a drawer can be revealed by an edge swipe from the left of the screen, and dismissed by swiping from the right edge of the drawer. However, because many users will rely on Up navigation to open a drawer, it is only suitable for use at the topmost level of your app's hierarchy.</p>
+
+<p><strong>Open a drawer from the main action bar if</strong>:</p>
+<ul>
+<li>You don't want to give up the vertical screen real estate for a dedicated tab bar.</li>
+<li>You want to provide direct navigation to a number of views within your app which don't have direct relationships between each other.</li>
+</ul>
+
+  </div>
+  <div class="layout-content-col span-7">
+    <img src="{@docRoot}design/media/actionbar_drawer.png">
+  </div>
+</div>
+
+<h2>Action buttons</h2>
 <p><em>Action buttons</em> on the action bar surface your app's most important activities. Think about which
 buttons will get used most often, and order them accordingly. Depending on available screen real
 estate, the system shows your most important actions as action buttons and moves the rest to the
@@ -283,7 +293,7 @@
 </p>
 <p>
 
-<a href="https://dl-ssl.google.com/android/design/Android_Design_Icons_20120229.zip">Download the Action Bar Icon Pack</a>
+<a href="{@docRoot}downloads/design/Android_Design_Icons_20120229.zip">Download the Action Bar Icon Pack</a>
 
 </p>
 
diff --git a/docs/html/design/patterns/app-structure.jd b/docs/html/design/patterns/app-structure.jd
index e2398ed..a483522 100644
--- a/docs/html/design/patterns/app-structure.jd
+++ b/docs/html/design/patterns/app-structure.jd
@@ -185,28 +185,18 @@
 in a category view.</p>
 <h2 id="details">Details</h2>
 
-<p>The detail view allows you to view and act on your data. The layout of the detail view depends on
-the data type being displayed, and therefore differs widely among apps.</p>
+<p>The detail view allows you to view and act on your data. The layout of the detail view depends on the data type being displayed, and therefore differs widely among apps.</p>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-4">
 
 <h4>Layout</h4>
-<p>Consider the activities people will perform in the detail view and arrange the layout accordingly.
-For immersive content, make use of the lights-out mode to allow for distraction-free viewing of
-full-screen content.</p>
-
-    <img src="{@docRoot}design/media/app_structure_people_detail.png">
+<p>Consider the activities people will perform in the detail view and arrange the layout accordingly.</p>
 
   </div>
   <div class="layout-content-col span-9">
 
-    <img src="{@docRoot}design/media/app_structure_book_detail_page_flip.png">
-    <div class="figure-caption">
-      Google Books' detail view is all about replicating the experience of reading an actual book.
-      The page-flip animation reinforces that notion. To create an immersive experience the app
-      enters lights-out mode, which hides all system UI affordances.
-    </div>
+    <img src="{@docRoot}design/media/app_structure_people_detail.png">
 
     <div class="figure-caption">
       The purpose of the People app's detail view is to surface communication options. The list view
@@ -217,9 +207,25 @@
   </div>
 </div>
 
+<div class="layout-content-row">
+  <div class="layout-content-col span-4">
+
+<h4>Lights-out mode</h4>
+<p>Immersive content like media and games is best experienced full screen without distractions. But that doesn't mean you can't also offer actions on the content like sharing, commenting, or searching. If the user hasn't interacted with any of the controls after a short period of time, automatically fade away the action bar and all system UI affordances so the user can lean back and enjoy the content. We call this lights-out mode. Later, if the user wants to take some action, they can touch anywhere on the screen to exit lights-out mode and bring back the controls.</p>
+
+  </div>
+  <div class="layout-content-col span-9">
+
+    <img src="{@docRoot}design/media/app_structure_book_detail_page_flip.png">
+    <div class="figure-caption">
+      Google Books' detail view replicates the immersive experience of reading an actual book through lights-out mode and a page-flip animation.
+    </div>
+  </div>
+</div>
+
 <h4>Make navigation between detail views efficient</h4>
 <p>If your users are likely to want to look at multiple items in sequence, allow them to navigate
-between items from within the detail view. Use swipe views or other techniques, such as filmstrips,
+between items from within the detail view. Use swipe views or other techniques, such as thumbnail view controls,
 to achieve this.</p>
 
 <img src="{@docRoot}design/media/app_structure_gmail_swipe.png">
@@ -229,8 +235,8 @@
 
 <img src="{@docRoot}design/media/app_structure_gallery_filmstrip.png">
 <div class="figure-caption">
-  In addition to supporting swipe gestures to move left or right through images, Gallery provides a
-  filmstrip control that lets people quickly jump to specific images.
+  In addition to supporting swipe gestures to move left or right through pages, Magazines provides a
+  thumbnail view control that lets people quickly jump to specific pages.
 </div>
 
 <h2 id="checklist">Checklist</h2>
diff --git a/docs/html/design/patterns/confirming-acknowledging.jd b/docs/html/design/patterns/confirming-acknowledging.jd
new file mode 100644
index 0000000..ce0631b
--- /dev/null
+++ b/docs/html/design/patterns/confirming-acknowledging.jd
@@ -0,0 +1,69 @@
+page.title=Confirming &amp; Acknowledging
+@jd:body
+
+<p>In some situations, when a user invokes an action in your app, it's a good idea to <em>confirm</em> or <em>acknowledge</em> that action through text.</p>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-6">
+    <img src="{@docRoot}design/media/confirm_ack_confirming.png">
+    <p><strong>Confirming</strong> is asking the user to verify that they truly want to proceed with an action they just invoked. In some cases, the confirmation is presented along with a warning or critical information related to the action that they need to consider.</p>
+  </div>
+  <div class="layout-content-col span-6">
+    <img src="{@docRoot}design/media/confirm_ack_acknowledge.png">
+    <p><strong>Acknowledging</strong> is displaying text to let the user know that the action they just invoked has been completed. This removes uncertainty about implicit operations that the system is taking. In some cases, the acknowledgment is presented along with an option to undo the action.</p>
+  </div>
+</div>
+
+<p>Communicating to users in these ways can help alleviate uncertainty about things that have happened or will happen. Confirming or acknowledging can also prevent users from making mistakes they might regret.</p>
+
+<h2>When to Confirm or Acknowledge User Actions</h2>
+<p>Not all actions warrant a confirmation or an acknowledgment. Use this flowchart to guide your design decisions.</p>
+<img src="{@docRoot}design/media/confirm_ack_flowchart.png">
+
+<h2>Confirming</h2>
+<div class="layout-content-row">
+  <div class="layout-content-col span-6">
+    <h4>Example: Google Play Books</h4>
+    <img src="{@docRoot}design/media/confirm_ack_ex_books.png">
+    <p>In this example, the user has requested to delete a book from their Google Play library. An <a href="{@docRoot}design/building-blocks/dialogs.html#alerts">alert</a> appears to confirm this action because it's important to understand that the book will no longer be available from any device.</p>
+    <p>When crafting a confirmation dialog, make the title meaningful by echoing the requested action.</p>
+  </div>
+  <div class="layout-content-col span-7">
+    <h4>Example: Android Beam</h4>
+    <img src="{@docRoot}design/media/confirm_ack_ex_beam.png">
+    <p>Confirmations don't necessarily have to be presented in an alert with two buttons. After initiating Android Beam, the user is prompted to touch the content to be shared (in this example, it's a photo). If they decide not to proceed, they simply move their phone away.</p>
+  </div>
+</div>
+
+<h2>Acknowledging</h2>
+<div class="layout-content-row">
+  <div class="layout-content-col span-6">
+    <h4>Example: Abandoned Gmail draft saved</h4>
+    <img src="{@docRoot}design/media/confirm_ack_ex_draftsave.png">
+    <p>In this example, if the user navigates back or up from the Gmail compose screen, something possibly unexpected happens: the current draft is automatically saved. An acknowledgment in the form of a toast makes that apparent. It fades after a few seconds.</p>
+    <p>Undo isn't appropriate here because saving was initiated by the app, not the user. And it's quick and easy to resume composing the message by navigating to the list of drafts.</p>
+
+  </div>
+  <div class="layout-content-col span-6">
+    <h4>Example: Gmail conversation deleted</h4>
+    <img src="{@docRoot}design/media/confirm_ack_draft_deleted.png">
+    <p>After the user deletes a conversation from the list in Gmail, an acknowledgment appears with an undo option. The acknowledgment remains until the user takes an unrelated action, such as scrolling the list.</p>
+  </div>
+</div>
+
+<h2>No Confirmation or Acknowledgment</h2>
+<div class="layout-content-row">
+  <div class="layout-content-col span-6">
+    <h4>Example: +1'ing</h4>
+    <img style="padding: 33px 0 30px;" src="{@docRoot}design/media/confirm_ack_ex_plus1.png">
+    <p><strong>Confirmation is unnecessary</strong>. If the user +1'd by accident, it's not a big deal. They can just touch the button again to undo the action.</p>
+    <p><strong>Acknowledgment is unnecessary</strong>. The user will see the +1 button bounce and turn red. That's a very clear signal.</p>
+  </div>
+  <div class="layout-content-col span-7">
+    <h4>Example: Removing an app from the Home Screen</h4>
+    <img src="{@docRoot}design/media/confirm_ack_ex_removeapp.png">
+    <p><strong>Confirmation is unnecessary</strong>. This is a deliberate action: the user must drag and drop an item onto a relatively large and isolated target. Therefore, accidents are highly unlikely. But if the user regrets the decision, it only takes a few seconds to bring it back again.</p>
+    <p><strong>Acknowledgment is unnecessary</strong>. The user will know the app is gone from the Home Screen because they made it disappear by dragging it away.</p>
+
+  </div>
+</div>
\ No newline at end of file
diff --git a/docs/html/design/patterns/help.jd b/docs/html/design/patterns/help.jd
new file mode 100644
index 0000000..cdac54d
--- /dev/null
+++ b/docs/html/design/patterns/help.jd
@@ -0,0 +1,112 @@
+page.title=Help
+@jd:body
+
+<p>We wish we could guarantee that if you follow every piece of advice on this website, everyone will be able to learn and use your app without a hitch. Sadly, that's not the case.</p>
+
+<p>Some of your users will run into questions or problems along the way. They'll be looking for answers <strong>within your app</strong>, and if they don't find them quickly, they may leave and never come back.</p>
+
+<p>This page covers design patterns for making help accessible in your app and tips for creating help content for users who are eager for assistance.</p>
+
+<h2 id="your-app">Designing Help into Your App</h2>
+
+<h3>Don't show unsolicited help, except in very limited cases</h3>
+<p>Naturally, you want everyone to quickly learn the ropes, discover the cool features, and get the most out of your app. So you might be tempted to present a one-time introductory slideshow, video, or splash screen to all new users when they first open the app. Or you might be drawn to the idea of displaying helpful text bubbles or dialogs when users interact with certain features for the first time.</p>
+<p>In almost all cases, we advise <strong>against</strong> approaches like these because:</p>
+<ul>
+  <li><strong>They're interruptions.</strong> People will be eager to start using your app, and anything you put in front of them will feel like an obstacle or possibly an annoyance, despite your good intentions. And because they didn't ask for it, they probably won't pay close attention to it.</li>
+  <li><strong>They're usually not necessary.</strong> If you have usability concerns about an aspect of your app, don't just throw help at the problem. Try to solve it in the UI. Apply Android design patterns, styles, and building blocks, and you'll go a long way in reducing the need to educate your users.</li>
+</ul>
+<p>The only reason for showing pure help content to new users unsolicited is:<br>
+<em>To teach high value functionality that's only available through a gesture.</em></p>
+
+<p>For example, we use help content to teach users how to place apps on their Home Screen. This functionality is:</p>
+<div class="layout-content-row">
+  <div class="layout-content-col span-8">
+    <ul>
+      <li><strong>High value</strong>
+      <p style="margin-top:0;">Without it, users wouldn't be able to customize the most frequently visited Android screen to meet their needs.</p></li>
+      <li><strong>Available only through a gesture</strong>
+      <p style="margin-top:0;">Because there's no button or menu for it, users might not ever discover it on their own.</p></li>
+    </ul>
+    <p>However, not all high value gesture-only functionality needs a tutorial. For example, don't teach users how to scroll content. They already know how because it's a fundamental, system-wide interaction.</p>
+  </div>
+  <div class="layout-content-col span-5">
+    <img src="{@docRoot}design/media/help_cling.png">
+    <div class="figure-caption">
+      The first time each user visits the All Apps screen, a semi-transparent overlay appears to teach an important gesture.
+    </div>
+  </div>
+  <p class="clearfix">Bottom line: when it comes to offering help in your app, it's much better to <strong>let users come to you</strong> when they need it.</p>
+</div>
+
+<h3 id="standard-design">Follow the standard design for navigating to help</h3>
+
+<p>On every screen in your app, offer help in the <a href="{@docRoot}design/patterns/actionbar.html">action overflow</a>. Always make it the very last item in the menu and label it "Help".</p>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-7">
+    <img src="{@docRoot}design/media/help_overflow.png">
+  </div>
+  <div class="layout-content-col span-6">
+    <img src="{@docRoot}design/media/help_solo_overflow.png">
+    <div class="figure-caption">
+      Even if your screen has no other action overflow items, "Help" should appear there and not be promoted to the action bar.
+    </div>
+  </div>
+  <p>We've established this standard design so that when users are desperate for help, they won't have to hunt to find it (see design principle: <a href="{@docRoot}design/get-started/principles.html#give-me-tricks">Give me tricks that work everywhere</a>).</p>
+</div>
+
+<h3 id="help-urgent">Assume that every call for help is urgent</h3>
+
+<p>In addition to help, you might want to expose other information, such as copyright info, credits, terms of service, and privacy policy.</p>
+
+<p>Let users access this information through the Help menu item, but optimize the flow for people with urgent questions about how to do something or why something is happening in your app. The smaller subset of users who are looking for legal fine print or the names of the people who created the app won't be as burdened by taking a few extra steps.</p>
+
+<p>The same is true for any communication options you might want to provide, such as contacting customer support or submitting feedback. Offer these options in a way that doesn't add an extra step before users see help. When you put the help content forward, you increase the likelihood that users will find the answers on their own, which in turn reduces your support costs.</p>
+
+<p>When someone chooses "Help":</p>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-4">
+    <img src="{@docRoot}design/media/help_dont.png">
+  </div>
+  <div class="layout-content-col span-4">
+    <img src="{@docRoot}design/media/help_better.png">
+  </div>
+  <div class="layout-content-col span-5">
+    <img src="{@docRoot}design/media/help_evenbetter.png">
+  </div>  
+</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-4">
+    <h4 class="do-dont-label bad">Don't</h4>
+    <p>Present a dialog asking them to choose between help and other options.</p>
+  </div>
+  <div class="layout-content-col span-4">
+    <h4 class="do-dont-label good">Better</h4>
+    <p>Immediately launch a web browser with help content. Place other options in a footer.</p>
+  </div>
+  <div class="layout-content-col span-5">
+    <h4 class="do-dont-label good">Even Better</h4>
+    <p>Build a help screen in your app and offer other options in the action bar. For example, you could let users contact you with questions or feedback through an action button. The action overflow is the ideal place for non-help information that users rarely need.</p>
+    <p>This requires more development work than launching a web browser, but it's a nicer experience for users because they don't leave your app to get the help they need and doesn't require a network connection.</p>
+  </div>
+</div>
+
+<h2>Principles for Writing On-Screen Help Content</h2>
+
+<h4>Help is part of the UI</h4>
+<p>On-screen help is an extension of your app's UI, not a description of it. All words on the screen from the core app to the help should follow our <a href="{@docRoot}design/style/writing.html">Writing Style</a> principles so that the end-to-end experience feels seamless and cohesive.</p>
+
+<h4>Make every pixel count</h4>
+<p>It's not necessary to document every single detail about your app, especially things that are extremely apparent just by looking at the UI, or behaviors that are standard for the platform. Surface just the key additional information that the on-screen text doesn't have room to describe, in a way that makes it easy to map to the screen.</p>
+
+<h4>Pictures are faster than words</h4>
+<p>In describing key UI elements and providing step-by-step instructions, consider combining text with icons, partial screenshots with callouts, and other imagery. You'll need fewer words to explain things, and users will absorb the information more quickly.</p>
+
+<h4>Help me scan, not read</h4>
+<p>People don't read help from start to finish. They scan around, looking for a piece of information containing the answer they need. Make it less burdensome with friendly formatting and layout choices like bold headings, bulleted and numbered lists, tables, and white space between paragraphs. And if you have a large amount of content, divide it into multiple screens to cut down on scrolling.</p>
+
+<h4>Take me straight to the answer</h4>
+<p>What's better than a screen that's easy to scan? A screen that requires no scanning at all because the answer's right there. Consider having each screen in your app navigate to help that's relevant just to that screen. We call this <em>contextual help</em>, and it's the holy grail of user assistance. If you take this approach, be sure to also provide a way to get to the rest of the help content.</p>
\ No newline at end of file
diff --git a/docs/html/design/patterns/index.jd b/docs/html/design/patterns/index.jd
index 6f88e6d..4416de1 100644
--- a/docs/html/design/patterns/index.jd
+++ b/docs/html/design/patterns/index.jd
@@ -20,10 +20,10 @@
   <div id="text-overlay">
     Design apps that behave in a consistent, predictable fashion.
     <br><br>
-    <a href="{@docRoot}design/patterns/new-4-0.html" class="landing-page-link">New in Android 4.0</a>
+    <a href="{@docRoot}design/patterns/new.html" class="landing-page-link">New in Android</a>
   </div>
 
-  <a href="{@docRoot}design/patterns/new-4-0.html">
+  <a href="{@docRoot}design/patterns/new.html">
     <img src="{@docRoot}design/media/patterns_landing.png">
   </a>
 </div>
diff --git a/docs/html/design/patterns/multi-pane-layouts.jd b/docs/html/design/patterns/multi-pane-layouts.jd
index 0e63e32..ad888e9 100644
--- a/docs/html/design/patterns/multi-pane-layouts.jd
+++ b/docs/html/design/patterns/multi-pane-layouts.jd
@@ -26,8 +26,8 @@
 relationship between the panels.</p>
 <h2 id="orientation">Compound Views and Orientation Changes</h2>
 
-<p>Screens should have the same functionality regardless of orientation. If you use a compound view in
-one orientation, don't split it up when the user rotates the screen. There are several techniques
+<p>Screens should strive to have the same functionality regardless of orientation. If you use a compound view in
+one orientation, try not to split it up when the user rotates the screen. There are several techniques
 you can use to adjust the layout after orientation change while keeping functional parity intact.</p>
 
 <div class="layout-content-row">
@@ -67,9 +67,7 @@
   <div class="layout-content-col span-5">
 
 <h4>Expand/collapse</h4>
-<p>When the device rotates, collapse the left pane view to only show the most important information.
-Provide an <em>expand</em> control that allows the user to bring the left pane content back to its original
-width and vice versa.</p>
+<p>When the device rotates, collapse the left pane view to only show the most important information.</p>
 
   </div>
 </div>
@@ -83,9 +81,7 @@
   <div class="layout-content-col span-5">
 
 <h4>Show/hide</h4>
-<p>After rotating the device, show the right pane in fullscreen view. Use the Up icon in the action bar
-to show the left panel and allow navigation to a different email. Hide the left panel by touching
-the content in the detail panel.</p>
+<p>If your screen cannot accommodate the compound view on rotation show the right pane in full screen view on rotation to portrait. Use the Up icon in action bar to show the parent screen.</p>
 
   </div>
 </div>
@@ -104,7 +100,7 @@
 <p>Look for opportunities to consolidate your views into multi-panel compound views.</p>
 </li>
 <li>
-<p>Make sure that your screens provide functional parity after the screen orientation
+<p>Make sure that your screens try to provide functional parity after the screen orientation
   changes.</p>
 </li>
 </ul>
diff --git a/docs/html/design/patterns/new-4-0.jd b/docs/html/design/patterns/new-4-0.jd
deleted file mode 100644
index 91ebba7..0000000
--- a/docs/html/design/patterns/new-4-0.jd
+++ /dev/null
@@ -1,71 +0,0 @@
-page.title=New in Android 4.0
-@jd:body
-
-<div class="layout-content-row">
-  <div class="layout-content-col span-7">
-
-<h4>Navigation bar</h4>
-<p>Android 4.0 removes the need for traditional hardware keys on phones by replacing them with a
-virtual navigation bar that houses the Back, Home and Recents buttons. Read the
-<a href="{@docRoot}design/patterns/compatibility.html">Compatibility</a> pattern to learn how the OS adapts to
-phones with hardware buttons and how pre-Android 3.0 apps that rely on menu keys are supported.</p>
-
-  </div>
-  <div class="layout-content-col span-6">
-
-    <img src="{@docRoot}design/media/whats_new_nav_bar.png">
-
-  </div>
-</div>
-
-<div class="vspace size-2">&nbsp;</div>
-
-<div class="layout-content-row">
-  <div class="layout-content-col span-7">
-
-<h4>Action bar</h4>
-<p>The action bar is the most important structural element of an Android app. It provides consistent
-navigation across the platform and allows your app to surface actions.</p>
-
-  </div>
-  <div class="layout-content-col span-6">
-
-    <img src="{@docRoot}design/media/whats_new_action_bar.png">
-
-  </div>
-</div>
-
-<div class="vspace size-2">&nbsp;</div>
-
-<div class="layout-content-row">
-  <div class="layout-content-col span-7">
-
-<h4>Multi-pane layouts</h4>
-<p>Creating apps that scale well across different form factors and screen sizes is important in the
-Android world. Multi-pane layouts allow you to combine different activities that show separately on
-smaller devices into richer compound views for tablets.</p>
-
-  </div>
-  <div class="layout-content-col span-6">
-
-    <img src="{@docRoot}design/media/whats_new_multipanel.png">
-
-  </div>
-</div>
-
-<div class="vspace size-2">&nbsp;</div>
-
-<div class="layout-content-row">
-  <div class="layout-content-col span-7">
-
-<h4>Selection</h4>
-<p>The long press gesture which was traditionally used to show contextual actions for objects is now
-used for data selection. When selecting data, contextual action bars allow you to surface actions.</p>
-
-  </div>
-  <div class="layout-content-col span-6">
-
-    <img src="{@docRoot}design/media/whats_new_multiselect.png">
-
-  </div>
-</div>
diff --git a/docs/html/design/patterns/new.jd b/docs/html/design/patterns/new.jd
new file mode 100644
index 0000000..1fc4987
--- /dev/null
+++ b/docs/html/design/patterns/new.jd
@@ -0,0 +1,114 @@
+page.title=New in Android
+@jd:body
+
+<h2>Jelly Bean - Android 4.1</h2>
+
+<h4>Notifications</h4>
+<div class="layout-content-row">
+  <div class="layout-content-col span-7">
+    <p>Notifications have received some notable enhancements in Android 4.1:</p>
+    <ul>
+      <li>Users can act on notifications immediately from the drawer</li>
+      <li>Notifications are more flexible in size and layout</li>
+      <li>A priority flag helps sort notifications by importance</li>
+      <li>Notifications can be collapsed and expanded</li>
+    </ul>
+
+    <p>The base notification layout has not changed, so app notifications designed for versions earlier than Jelly Bean still look and work the same. Check the updated <a href="{@docRoot}design/patterns/notifications.html">Notifications</a> page for more details.</p>
+  </div>
+  <div class="layout-content-col span-6">
+    <img src="{@docRoot}design/media/new_notifications.png">
+  </div>
+</div>
+
+<div class="vspace size-2">&nbsp;</div>
+
+<h4>Resizable Application Widgets</h4>
+<div class="layout-content-row">
+  <div class="layout-content-col span-7">
+    <p>Widgets are an essential aspect of home screen customization, allowing "at-a-glance" views of an app's most important data and functionality right from the user's home screen. Android 4.1 introduces improved App Widgets that can <strong>automatically resize and load different content</strong> based upon a number of factors including:</p>
+    <ul>
+      <li>Where the user drops them on the home screen</li>
+      <li>The size to which the user expands them</li>
+      <li>The amount of room available on the home screen</li>
+    </ul>
+
+    <p>You can supply separate landscape and portrait layouts for your widgets, which the system inflates as appropriate when the screen orientation changes. The Application Widgets has useful details about widget types, limitations, and design considerations.</p>
+  </div>
+  <div class="layout-content-col span-6">
+    <img src="{@docRoot}design/media/new_widgets.png">
+  </div>
+</div>
+
+<div class="vspace size-2">&nbsp;</div>
+
+<h4>Accessibility</h4>
+<div class="layout-content-row">
+  <div class="layout-content-col span-11">
+    <p>One of Android's missions is to organize the world's information and make it universally accessible and useful. Our mission applies to all users-including people with disabilities such as visual impairment, color deficiency, hearing loss, and limited dexterity.</p>
+    <p>The new <a href="{@docRoot}design/patterns/accessibility.html">Accessibility</a> page provides details on how to design your app to be as accessible as possible by:</p>
+    <ul>
+      <li>Making navigation intuitive</li>
+      <li>Using recommended touch target sizes</li>
+      <li>Labeling visual UI elements meaningfully</li>
+      <li>Providing alternatives to affordances that time out</li>
+      <li>Using standard framework controls or enable TalkBack for custom controls</li>
+      <li>Trying it out yourself</li>
+    </ul>
+
+    <p>You can supply separate landscape and portrait layouts for your widgets, which the system inflates as appropriate when the screen orientation changes. The [Application Widgets] (should be link) has useful details about widget types, limitations, and design considerations.</p>
+  </div>
+  <div class="layout-content-col span-2">
+    <img src="{@docRoot}design/media/new_accessibility.png">
+  </div>
+</div>
+
+<h2>Ice Cream Sandwich - Android 4.0</h2>
+
+<h4>Navigation bar</h4>
+<div class="layout-content-row">
+  <div class="layout-content-col span-7">
+    <p>Android 4.0 removes the need for traditional hardware keys on phones by replacing them with a
+    virtual navigation bar that houses the Back, Home and Recents buttons. Read the <a href="{@docRoot}design/patterns/compatibility.html">Compatibility</a> pattern to learn how the OS adapts to phones with hardware buttons and how pre-Android 3.0 apps that rely on menu keys are supported.</p>
+  </div>
+  <div class="layout-content-col span-6">
+    <img src="{@docRoot}design/media/whats_new_nav_bar.png">
+  </div>
+</div>
+
+<div class="vspace size-2">&nbsp;</div>
+
+<h4>Action bar</h4>
+<div class="layout-content-row">
+  <div class="layout-content-col span-7">
+    <p>The action bar is the most important structural element of an Android app. It provides consistent navigation across the platform and allows your app to surface actions.</p>
+  </div>
+  <div class="layout-content-col span-6">
+    <img src="{@docRoot}design/media/whats_new_action_bar.png">
+  </div>
+</div>
+
+<div class="vspace size-2">&nbsp;</div>
+
+<h4>Multi-pane layouts</h4>
+<div class="layout-content-row">
+  <div class="layout-content-col span-7">
+    <p>Creating apps that scale well across different form factors and screen sizes is important in the Android world. Multi-pane layouts allow you to combine different activities that show separately on smaller devices into richer compound views for tablets.</p>
+  </div>
+  <div class="layout-content-col span-6">
+    <img src="{@docRoot}design/media/whats_new_multipanel.png">
+  </div>
+</div>
+
+<div class="vspace size-2">&nbsp;</div>
+
+<h4>Selection</h4>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-7">
+    <p>The long press gesture which was traditionally used to show contextual actions for objects is now used for data selection. When selecting data, contextual action bars allow you to surface actions.</p>
+  </div>
+  <div class="layout-content-col span-6">
+    <img src="{@docRoot}design/media/whats_new_multiselect.png">
+  </div>
+</div>
diff --git a/docs/html/design/patterns/notifications.jd b/docs/html/design/patterns/notifications.jd
index ad88a01..75bfff2 100644
--- a/docs/html/design/patterns/notifications.jd
+++ b/docs/html/design/patterns/notifications.jd
@@ -1,20 +1,160 @@
 page.title=Notifications
 @jd:body
 
-<p>The notification system allows your app to keep the user informed about important events, such as
-new messages in a chat app or a calendar event.</p>
-<p>To create an app that feels streamlined, pleasant, and respectful, it is important to design your
-notifications carefully. Notifications embody your app's voice, and contribute to your app's
-personality. Unwanted or unimportant notifications can annoy the user, so use them judiciously.</p>
+<p>The notification system allows your app to keep the user informed about events, such as new chat messages or a calendar event. Think of notifications as a news channel that alerts the user to important events as they happen or a log that chronicles events while the user is not paying attention.</p>
+
+<h4>New in Jelly Bean</h4>
+<p>In Jelly Bean, notifications received their most important structural and functional update since the beginning of Android.</p>
+<ul>
+  <li>Notifications can include actions that enable the user to immediately act on a notification from the notification drawer.</li>
+  <li>Notifications are now more flexible in size and layout. They can be expanded to show additional information details.</li>
+  <li>A priority flag was introduced that helps to sort notifications by importance rather than time only.</li>
+</ul>
+
+<h2>Anatomy of a notification</h2>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-6">
+    <h4>Base Layout</h4>
+    <p>At a minimum, all notifications consist of a base layout, including:</p>
+    <ul>
+      <li>the sending application's notification icon or the sender's photo</li>
+      <li>a notification title and message</li>
+      <li>a timestamp</li>
+      <li>a secondary icon to identify the sending application when the senders image is shown for the main icon</li>
+    </ul>
+    <p>The information arrangement of the base layout has not changed in Jelly Bean, so app notifications designed for versions earlier than Jelly Bean still look and work the same.</p>
+  </div>
+  <div class="layout-content-col span-6">
+    <img src="{@docRoot}design/media/notifications_pattern_anatomy.png">
+    <div class="figure-caption">
+      Base layout of a notification
+    </div>
+  </div>
+</div>
+
+<h4>Expanded layouts</h4>
+<p>With Jelly Bean you have the option to provide more event detail. You can use this to show the first few lines of a message or show a larger image preview. This provides the user with additional context, and - in some cases - may allow the user to read a message in its entirety. The user can pinch-zoom or two-finger glide in order to toggle between base and expanded layouts. For single event notifications, Android provides two expanded layout templates (text and image) for you to re-use in your application.</p>
+
+<img src="{@docRoot}design/media/notifications_pattern_expandable.png">
+
+<h4>Actions</h4>
+<div class="layout-content-row">
+  <div class="layout-content-col span-6">
+    <p>Starting with Jelly Bean, Android supports optional actions that are displayed at the bottom of the notification. With actions, users can handle the most common tasks for a particular notification from within the notification shade without having to open the originating application. This speeds up interaction and, in conjunction with "swipe-to-dismiss", helps users to streamline their notification triaging experience.</p>
+    <p>Be judicious with how many actions you include with a notification. The more actions you include, the more cognitive complexity you create. Limit yourself to the fewest number of actions possible by only including the most imminently important and meaningful ones.</p>
+    <p>Good candidates for actions on notifications are actions that are:</p>
+    <ul>
+      <li>essential, frequent and typical for the content type you're displaying</li>
+      <li>time-critical</li>
+      <li>not overlapping with neighboring actions</li>
+    </ul>
+    <p>Avoid actions that are:</p>
+    <ul>
+      <li>ambiguous</li>
+      <li>duplicative of the default action of the notification (such as "Read" or "Open")</li>
+    </ul>
+  </div>
+  <div class="layout-content-col span-7">
+    <img src="{@docRoot}design/media/notifications_pattern_two_actions.png">
+    <div class="figure-caption">
+      Calendar reminder notification with two actions
+    </div>
+  </div>
+</div>
+
+<p>You can specify a maximum of three actions, each consisting of an action icon and an action name. Adding actions to a simple base layout will make the notification expandable, even if the notification doesn't have an expanded layout. Since actions are only shown for expanded notifications and are otherwise hidden, you must make sure that any action a user can invoke from a notification is available from within the associated application as well.</p>
+
+<h2>Design guidelines</h2>
+<div class="layout-content-row">
+  <div class="layout-content-col span-6">
+    <img src="{@docRoot}design/media/notifications_pattern_personal.png">
+  </div>
+  <div class="layout-content-col span-7">
+    <h4>Make it personal</h4>
+    <p>For notifications of items sent by another user (such as a message or status update), include that person's image.</p>
+    <p>Remember to include the app icon as a secondary icon in the notification, so that the user can still identify which app posted it.</p>
+  </div>
+</div>
+
+<h4>Navigate to the right place</h4>
+<p>When the user touches the body of a notification (outside of the action buttons), open your app to the place where the user can consume and act upon the data referenced in the notification. In most cases this will be the detail view of a
+single data item such as a message, but it might also be a summary view if the notification is stacked (see <em>Stacked notifications</em> below) and references multiple items. If in any of those cases the user is taken to a hierarchy level below your app's top-level, insert navigation into your app's back stack to allow them to navigate to your app's top level using the system back key. For more
+information, see the chapter on <em>System-to-app navigation</em> in the <a href="{@docRoot}design/patterns/navigation.html">Navigation</a> design pattern.</p>
+
+<h4>Correctly set and manage notification priority</h4>
+<p>Starting with Jelly Bean, Android now supports a priority flag for notifications. It allows you to influence where your notification will appear in comparison to other notifications and help to make sure that users always see their most important notifications first. You can choose from the following priority levels when posting a notification:</p>
+
+<table>
+  <tr>
+    <th><strong>Priority</strong></th>
+    <th><strong>Use</strong></th>
+  </tr>
+  <tr>
+    <td>MAX</td>
+    <td>Use for critical and urgent notifications that alert the user to a condition that is time-critical or needs to be resolved before they can continue with a particular task.</td>
+  </tr>
+  <tr>
+    <td>HIGH</td>
+    <td>Use high priority notifications primarily for important communication, such as message or chat events with content that is particularly interesting for the user.</td>
+  </tr>
+  <tr>
+    <td>DEFAULT</td>
+    <td>The default priority. Keep all notifications that don't fall into any of the other categories at this priority level.</td>
+  </tr>
+  <tr>
+    <td>LOW</td>
+    <td>Use for notifications that you still want the user to be informed about, but that rate low in urgency.</td>
+  </tr>
+  <tr>
+    <td>MIN</td>
+    <td>Contextual/background information (e.g. weather information, contextual location information). Minimum     priority notifications will not show in the status bar. The user will only discover them when they expand the notification tray.</td>
+  </tr>
+</table>
+<img src="{@docRoot}design/media/notifications_pattern_priority.png">
+
+<h4>Stack your notifications</h4>
+<p>If your app creates a notification while another of the same type is still pending, avoid creating
+an altogether new notification object. Instead, stack the notification.</p>
+<p>A stacked notification builds a summary description and allows the user to understand how many
+notifications of a particular kind are pending.</p>
+<p><strong>Don't</strong>:</p>
+
+<img src="{@docRoot}design/media/notifications_pattern_additional_fail.png">
+
+<p><strong>Do</strong>:</p>
+
+<img src="{@docRoot}design/media/notifications_pattern_additional_win.png">
+
+<p>You can provide more detail about the individual notifications that make up a stack by using the expanded digest layout. This allows users to gain a better sense of which notifications are pending and if they are interesting enough to be read in detail within the associated app.</p>
+
+<img src="{@docRoot}design/media/notifications_expand_contract_msg.png">
+
+<h4>Make notifications optional</h4>
+<p>Users should always be in control of notifications. Allow the user to disable your apps notifications or change their alert properties, such as alert sound and whether to use vibration, by adding a notification settings item to your application settings.</p>
+<h4>Use distinct icons</h4>
+<p>By glancing at the notification area, the user should be able to discern what kinds of notifications are currently pending.</p>
+
+<div class="do-dont-label good"><strong>Do</strong></div>
+<p style="margin-top:0;">Look at the notification icons the Android apps already provide and create notification icons for your app that are sufficiently distinct in appearance.</p>
+<div class="do-dont-label good"><strong>Do</strong></div>
+<p style="margin-top:0;">Use the proper <a href="{@docRoot}design/style/iconography.html#notification">notification icon style</a> for small icons, and the Holo Dark <a href="{@docRoot}design/style/iconography.html#action-bar">action bar icon style</a> for your action icons.</p>
+<div class="do-dont-label good"><strong>Do</strong></div>
+<p style="margin-top:0;">Keep your icons visually simple and avoid excessive detail that is hard to discern.</p>
+<div class="do-dont-label bad"><strong>Don't</strong></div>
+<p style="margin-top:0;">Use color to distinguish your app from others.</p>
+
+<h4>Pulse the notification LED appropriately</h4>
+<p>Many Android devices contain a tiny lamp, called the notification <acronym title="Light-Emitting Diode">LED</acronym>, which is used to keep the user informed about events while the screen is off. Notifications with a priority level of MAX, HIGH, or DEFAULT should cause the LED to glow, while those with lower priority (LOW and MIN) should not.</p>
+
+<p>The user's control over notifications should extend to the LED. By default, the LED will glow with a white color. Your notifications shouldn't use a different color unless the user has explicitly customized it.</p>
+
+<h2>Building notifications that users care about</h2>
+<p>To create an app that feels streamlined, pleasant, and respectful, it is important to design your notifications carefully. Notifications embody your app's voice, and contribute to your app's personality. Unwanted or unimportant notifications can annoy the user, so use them judiciously.</p>
+
 <h4>When to display a notification</h4>
-<p>To create an application that people love, it's important to recognize that the user's attention and
-focus is a resource that must be protected. To use an analogy that might resonate with software
-developers, the user is not a method that can be invoked to return a value.  The user's focus is a
-resource more akin to a thread, and creating a notification momentarily blocks the user thread as
-they process and then dismiss the interruptive notification.</p>
-<p>Android's notification system has been designed to quickly inform users of events while they focus
-on a task, but it is nonetheless still important to be conscientious when deciding to create a
-notification.</p>
+<p>To create an application that people love, it's important to recognize that the user's attention and focus is a resource that must be protected. While Android's notification system has been designed to minimize the impact of notifications on the users attention, it is nonetheless still important to be aware of the fact that notifications are potentially interrupting the users task flow. As you plan your notifications, ask yourself if they are important enough to warrant an interruption. If you are unsure, allow the user to opt into a notification using your apps notification settings or adjust the notifications priority flag.</p>
+<p>Time sensitive events are great opportunities for valuable notifications with high priority, especially if these synchronous events involve other people. For instance, an incoming chat is a real time and synchronous form of communication: there is another user actively waiting on you to respond. Calendar events are another good example of when to use a notification and grab the user's attention, because the event is imminent, and calendar events often involve other people.</p>
 <p>While well behaved apps generally only speak when spoken to, there are some limited cases where an
 app actually should interrupt the user with an unprompted notification.</p>
 <p>Notifications should be used primarily for <strong>time sensitive events</strong>, and especially if these
@@ -34,27 +174,19 @@
 <p>There are however many other cases where notifications should not be used:</p>
 <ul>
 <li>
-<p>Don't notify the user of information that is not directed specifically at them, or information
-that is not truly time sensitive.  For instance the asynchronous and undirected updates flowing
-through a social network do not warrant a real time interruption.</p>
+<p>Avoid notifying the user of information that is not directed specifically at them, or information that is not truly time sensitive. For instance the asynchronous and undirected updates flowing through a social network generally do not warrant a real time interruption. For the users that do care about them, allow them to opt-in.</p>
 </li>
 <li>
-<p>Don't create a notification if the relevant new information is currently on screen. Instead, use
-the UI of the application itself to notify the user of new information directly in context. For
-instance, a chat application should not create system notifications while the user is actively
-chatting with another user.</p>
+<p>Don't create a notification if the relevant new information is currently on screen. Instead, use the UI of the application itself to notify the user of new information directly in context. For instance, a chat application should not create system notifications while the user is actively chatting with another user.</p>
 </li>
 <li>
-<p>Don't interrupt the user for low level technical operations, like saving or syncing information,
-or updating an application, if it is possible for the system to simply take care of itself without
-involving the user.</p>
+<p>Don't interrupt the user for low level technical operations, like saving or syncing information, or updating an application, if it is possible for the system to simply take care of itself without involving the user.</p>
 </li>
 <li>
-<p>Don't interrupt the user to inform them of an error if it is possible for the application to
-quickly recover from the error on its own without the user taking any action.</p>
+<p>Don't interrupt the user to inform them of an error if it is possible for the application to quickly recover from the error on its own without the user taking any action.</p>
 </li>
 <li>
-<p>Don't use notifications for services that the user cannot manually start or stop.</p>
+<p>Don't create notifications that have no true notification content and merely advertise your app. A notification should inform the user about a state and should not be used to merely launch an app.</p>
 </li>
 <li>
 <p>Don't create superfluous notifications just to get your brand in front of users. Such
@@ -66,108 +198,10 @@
 
   </div>
   <div class="layout-content-col span-6">
-
     <img src="{@docRoot}design/media/notifications_pattern_social_fail.png">
-
   </div>
 </div>
 
-<h2 id="design-guidelines">Design Guidelines</h2>
-
-<div class="layout-content-row">
-  <div class="layout-content-col span-6">
-
-    <img src="{@docRoot}design/media/notifications_pattern_anatomy.png">
-
-  </div>
-  <div class="layout-content-col span-6">
-
-<h4>Make it personal</h4>
-<p>For notifications of items sent by another user (such as a message or status update), include that
-person's image.</p>
-<p>Remember to include the app icon as a secondary icon in the notification, so that the user can
-still identify which app posted it.</p>    
-
-  </div>
-</div>
-
-<h4>Navigate to the right place</h4>
-<p>When the user touches a notification, be open your app to the place where the user can consume and
-act upon the data referenced in the notification. In most cases this will be the detail view of a
-single data item (e.g. a message), but it might also be a summary view if the notification is
-stacked (see <em>Stacked notifications</em> below) and references multiple items. If in any of those cases
-the user is taken to a hierarchy level below your app's top-level, insert navigation into your app's
-back stack to allow them to navigate to your app's top level using the system back key. For more
-information, see the chapter on <em>System-to-app navigation</em> in the
-<a href="{@docRoot}design/patterns/navigation.html">Navigation</a> design pattern.</p>
-<h4>Timestamps for time sensitive events</h4>
-<p>By default, standard Android notifications include a timestamp in the upper right corner. Consider
-whether the timestamp is valuable in the context of your notification. If the timestamp is not
-valuable, consider if the event is important enough to warrant grabbing the user's attention with a
-notification. If the notification is important enough, decide if you would like to opt out of
-displaying the timestamp.</p>
-<p>Include a timestamp if the user likely needs to know how long ago the notification occurred. Good
-candidates for timestamps include communication notifications (email, messaging, chat, voicemail)
-where the user may need the timestamp information to understand the context of a message or to
-tailor a response.</p>
-<h4>Stack your notifications</h4>
-<p>If your app creates a notification while another of the same type is still pending, avoid creating
-an altogether new notification object. Instead, stack the notification.</p>
-<p>A stacked notification builds a summary description and allows the user to understand how many
-notifications of a particular kind are pending.</p>
-<p><strong>Don't</strong>:</p>
-
-<img src="{@docRoot}design/media/notifications_pattern_additional_fail.png">
-
-<p><strong>Do</strong>:</p>
-
-<img src="{@docRoot}design/media/notifications_pattern_additional_win.png">
-
-<p>If you keep the summary and detail information on different screens, a stacked notification may need
-to open to a different place in the app than a single notification.</p>
-<p>For example, a single email notification should always open to the content of the email, whereas a
-stacked email notification opens to the Inbox view.</p>
-<h4>Clean up after yourself</h4>
-<p>Just like calendar events, some notifications alert the user to an event that happens at a
-particular point in time. After that moment has passed, the notification is likely not important to
-the user anymore, and you should consider removing it automatically.  The same is true for active
-chat conversations or voicemail messages the user has listened to, users should not have to manually
-dismiss notifications independently from taking action on them.</p>
-
-<div class="vspace size-1">&nbsp;</div>
-
-<div class="layout-content-row">
-  <div class="layout-content-col span-7">
-
-<h4>Provide a peek into your notification</h4>
-<p>You can provide a short preview of your notification's content by providing optional ticker text.
-The ticker text is shown for a short amount of time when the notification enters the system and then
-hides automatically.</p>
-
-  </div>
-  <div class="layout-content-col span-6">
-
-    <img src="{@docRoot}design/media/notifications_pattern_phone_ticker.png">
-
-  </div>
-</div>
-
-<h4>Make notifications optional</h4>
-<p>Users should always be in control of notifications. Allow the user to silence the notifications from
-your app by adding a notification settings item to your application settings.</p>
-<h4>Use distinct icons</h4>
-<p>By glancing at the notification area, the user should be able to discern what notification types are
-currently pending.</p>
-<p><strong>Do</strong>:</p>
-<ul>
-<li>Look at the notification icons the Android apps already provide and create notification icons for
-  your app that are sufficiently distinct in appearance.</li>
-</ul>
-<p><strong>Don't</strong>:</p>
-<ul>
-<li>Use color to distinguish your app from others. Notification icons should generally be monochrome.</li>
-</ul>
-
 <h2 id="interacting-with-notifications">Interacting With Notifications</h2>
 
 <div class="layout-content-row">
@@ -178,11 +212,8 @@
   </div>
   <div class="layout-content-col span-6">
 
-<p>Notifications are indicated by icons in the notification area and can be accessed by opening the
-notification drawer.</p>
-<p>Inside the drawer, notifications are chronologically sorted with the latest one on top. Touching a
-notification opens the associated app to detailed content matching the notification. Swiping left or
-right on a notification removes it from the drawer.</p>
+  <p>Notifications are indicated by icons in the notification area and can be accessed by opening the notification drawer.</p>
+  <p>Inside the drawer, notifications are chronologically sorted with the latest one on top. Touching a notification opens the associated app to detailed content matching the notification. Swiping left or right on a notification removes it from the drawer.</p>
 
   </div>
 </div>
@@ -190,8 +221,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-6">
 
-<p>On tablets, the notification area is integrated with the system bar at the bottom of the screen. The
-notification drawer is opened by touching anywhere inside the notification area.</p>
+<p>On tablets, the notification area is integrated with the system bar at the bottom of the screen. The notification drawer is opened by touching anywhere inside the notification area.</p>
 
   </div>
   <div class="layout-content-col span-6">
@@ -210,27 +240,14 @@
   <div class="layout-content-col span-6">
 
 <h4>Ongoing notifications</h4>
-<p>Ongoing notifications keep users informed about an ongoing process in the background. For example,
-music players announce the currently playing track in the notification system and continue to do so
-until the user stops the playback. They can also be used to show the user feedback for longer tasks
-like downloading a file, or encoding a video. Ongoing notifications cannot be manually removed from
-the notification drawer.</p>
+<p>Ongoing notifications keep users informed about an ongoing process in the background. For example, music players announce the currently playing track in the notification system and continue to do so until the user stops the playback. They can also be used to show the user feedback for longer tasks like downloading a file, or encoding a video. Ongoing notifications cannot be manually removed from the notification drawer.</p>
 
   </div>
 </div>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-12">
-
-<h4>Dialogs and toasts are for feedback not notification</h4>
-<p>Your app should not create a dialog or toast if it is not currently on screen. Dialogs and Toasts
-should only be displayed as the immediate response to the user taking an action inside of your app.
-For instance, dialogs can be used to confirm that the user understands the severity of an action,
-and toasts can echo back that an action has been successfully taken.</p>
-
+    <h4>Dialogs and toasts are for feedback not notification</h4>
+    <p>Your app should not create a dialog or toast if it is not currently on screen. Dialogs and Toasts should only be displayed as the immediate response to the user taking an action inside of your app. For further guidance on the use of dialogs and toasts, refer to <a href="{@docRoot}design/patterns/confirming-acknowledging.html">Confirming &amp; Acknowledging</a>.</p>
   </div>
-</div>
-
-<div class="vspace size-1">&nbsp;</div>
-
-<img src="{@docRoot}design/media/notifications_pattern_dialog_toast.png">
+</div>
\ No newline at end of file
diff --git a/docs/html/design/patterns/selection.jd b/docs/html/design/patterns/selection.jd
index e3ee90e..612c370 100644
--- a/docs/html/design/patterns/selection.jd
+++ b/docs/html/design/patterns/selection.jd
@@ -1,8 +1,7 @@
 page.title=Selection
 @jd:body
 
-<p>Android 3.0 introduced the <em>long press</em> gesture&mdash;that is, a touch that's held in the same
-position for a moment&mdash;as the global gesture to select data. This affects the way you should
+<p>Android 3.0 changed the <em>long press</em> gesture&mdash;that is, a touch that's held in the same position for a moment&mdash;to be the global gesture to select data.. This affects the way you should
 handle multi-select and contextual actions in your apps.</p>
 
 <div class="vspace size-1">&nbsp;</div>
diff --git a/docs/html/design/patterns/settings.jd b/docs/html/design/patterns/settings.jd
index 3b28b84..d10f0d3 100644
--- a/docs/html/design/patterns/settings.jd
+++ b/docs/html/design/patterns/settings.jd
@@ -429,7 +429,7 @@
     <tbody>
       <tr>
         <td class="secondary-text">
-        After 10 minutes of activity
+        After 10 minutes of inactivity
         </td>
       </tr>
     </tbody>
@@ -551,7 +551,7 @@
     <tbody>
       <tr>
         <td class="secondary-text">
-        After 10 minutes of activity
+        After 10 minutes of inactivity
         </td>
       </tr>
     </tbody>
diff --git a/docs/html/design/patterns/swipe-views.jd b/docs/html/design/patterns/swipe-views.jd
index 95d65dd..252343d 100644
--- a/docs/html/design/patterns/swipe-views.jd
+++ b/docs/html/design/patterns/swipe-views.jd
@@ -24,7 +24,12 @@
 
 <img src="{@docRoot}design/media/swipe_views2.png">
 <div class="figure-caption">
-  Navigating between consecutive Email messages using the swipe gesture.
+  Navigating between consecutive Email messages using the swipe gesture. If a view contains content that exceeds the width of the screen such as a wide Email message, make sure the user's initial swipes will scroll horizontally within the view. Once the end of the content is reached, an additional swipe should navigate to the next view. In addition, support the use of edge swipes to immediately navigate between views when content scrolls horizontally.
+</div>
+
+<img src="{@docRoot}design/media/swipe_views3.png">
+<div class="figure-caption">
+  Scrolling within a wide Email message using the swipe gesture before navigating to the next message.
 </div>
 
 <h2 id="between-tabs">Swiping Between Tabs</h2>
@@ -46,29 +51,29 @@
 
   </div>
   <div class="layout-content-col span-8">
+    <p>If your app uses action bar tabs, use swipe to navigate between the different views.</p>
+    <div class="vspace size-1">&nbsp;</div>
 
-<p>If your app uses action bar tabs, use swipe to navigate between the different views.</p>
-<div class="vspace size-2">&nbsp;</div>
-
-<h2 id="checklist">Checklist</h2>
-
-<ul>
-<li>
-<p>Use swipe to quickly navigate between detail views or tabs.</p>
-</li>
-<li>
-<p>Transition between the views as the user performs the swipe gesture. Do not wait for the
-  gesture to complete and then transition between views.</p>
-</li>
-<li>
-<p>If you used buttons in the past for previous/next navigation, replace them with
-  the swipe gesture.</p>
-</li>
-<li>
-<p>Consider adding contextual information in your detail view that informs the user about the
-  relative list position of the currently visible item.</p>
-</li>
-</ul>
-
+    <h2 id="checklist">Checklist</h2>
+    <ul>
+      <li>
+      <p>Use swipe to quickly navigate between detail views or tabs.</p>
+      </li>
+      <li>
+      <p>Transition between the views as the user performs the swipe gesture. Do not wait for the
+        gesture to complete and then transition between views.</p>
+      </li>
+      <li>
+      <p>If you used buttons in the past for previous/next navigation, replace them with
+        the swipe gesture.</p>
+      </li>
+      <li>
+      <p>Consider adding contextual information in your detail view that informs the user about the
+        relative list position of the currently visible item.</p>
+      </li>
+      <li>
+      <p>For more details on how to build swipe views, read the developer documentation on <a href="{@docRoot}training/implementing-navigation/lateral.html#horizontal-paging">Implementing Lateral Navigation</a>.</p>
+      </li>
+    </ul>
   </div>
 </div>
diff --git a/docs/html/design/patterns/widgets.jd b/docs/html/design/patterns/widgets.jd
new file mode 100644
index 0000000..cf4c74f
--- /dev/null
+++ b/docs/html/design/patterns/widgets.jd
@@ -0,0 +1,131 @@
+page.title=Widgets
+@jd:body
+
+<p>Widgets are an essential aspect of home screen customization. You can imagine them as "at-a-glance" views of an app's most important data and functionality that is accessible right from the user's home screen. Users can move widgets across their home screen panels, and, if supported, resize them to tailor the amount of information within a widget to their preference.</p>
+
+<h2>Widget types</h2>
+<p>As you begin planning your widget, think about what kind of widget you're trying to build. Widgets typically fall into one of the following categories:</p>
+
+<h3>Information widgets</h3>
+<img style="float:right;" src="{@docRoot}design/media/widgets_info.png">
+<p>Information widgets typically display a few crucial information elements that are important to a user and track how that information changes over time. Good examples for information widgets are weather widgets, clock widgets or sports score trackers. Touching information widgets typically launches the associated app and opens a detail view of the widget information.</p>
+
+<div class="vspace size-2">&nbsp;</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-6">
+    <h3>Collection widgets</h3>
+    <p>As the name implies, collection widgets specialize on displaying multitude elements of the same type, such as a collection of pictures from a gallery app, a collection of articles from a news app or a collection of emails/messages from a communication app. Collection widgets typically focus on two use cases: browsing the collection, and opening an element of the collection to its detail view for consumption. Collection widgets can scroll vertically.</p>
+  </div>
+  <div class="layout-content-col span-3">
+    <img src="{@docRoot}design/media/widgets_collection_gmail.png">
+    <div class="figure-caption">
+      ListView widget
+    </div>
+  </div>
+  <div class="layout-content-col span-4">
+    <img src="{@docRoot}design/media/widgets_collection_bookmarks.png">
+    <div class="figure-caption">
+      GridView widget
+    </div>
+  </div>
+</div>
+
+<h3>Control widgets</h3>
+<img style="float:right;" src="{@docRoot}design/media/widgets_control.png">
+<p>The main purpose of a control widget is to display often used functions that the user can trigger right from the home screen without having to open the app first. Think of them as remote controls for an app. A typical example of control widgets are music app widgets that allow the user to play, pause or skip music tracks from outside the actual music app.</p>
+<p>Interacting with control widgets may or may not progress to an associated detail view depending on if the control widget's function generated a data set, such as in the case of a search widget.</p>
+
+<div class="vspace size-2">&nbsp;</div>
+
+<h3>Hybrid widgets</h3>
+<img style="float:right;" src="{@docRoot}design/media/widgets_hybrid.png">
+<p>While all widgets tend to gravitate towards one of the three types described above, many widgets in reality are hybrids that combine elements of different types.</p>
+<p>For the purpose of your widget planning, center your widget around one of the base types and add elements of other types if needed.</p>
+<div class="figure-caption">
+A music player widget is primarily a control widget, but also keeps the user informed about what track is currently playing. It essentially combines a control widget with elements of an information widget type.
+</div>
+
+<h2>Widget limitations</h2>
+<p>While widgets could be understood as "mini apps", there are certain limitations that are important to understand before you start to embark on designing your widget:</p>
+
+<h3>Gestures</h3>
+<p>Because widgets live on the home screen, they have to co-exist with the navigation that is established there. This limits the gesture support that is available in a widget compared to a full-screen app. While apps for example may support a view pager that allows the user to navigate between screens laterally, that gesture is already taken on the home screen for the purpose of navigating between home panels.</p>
+<p>The only gestures available for widgets are:</p>
+<ul>
+  <li>Touch</li>
+  <li>Vertical swipe</li>
+</ul>
+<img src="{@docRoot}design/media/widgets_gestures.png">
+
+<h3>Elements</h3>
+<p>Given the above interaction limitations, some of the UI building blocks that rely on restricted gestures are not available for widgets. For a complete list of supported building blocks and more information on layout restrictions, please refer to the "Creating App Widget Layouts" section in the <a href="{@docRoot}guide/topics/appwidgets/index.html">App Widgets</a> API Guide.</p>
+
+<h2>Design guidelines</h2>
+
+<h3>Widget content</h3>
+<p>Widgets are a great mechanism to attract a user to your app by "advertising" new and interesting content that is available for consumption in your app.</p>
+<p>Just like the teasers on the front page of a newspaper, widgets should consolidate and concentrate an app's information and then provide a connection to richer detail within the app; or in other words: the widget is the information "snack" while the app is the "meal." As a bottom line, always make sure that your app shows more detail about an information item than what the widget already displays.</p>
+
+<h3>Widget navigation</h3>
+<p>Besides the pure information content, you should also consider to round out your widget's offering by providing navigation links to frequently used areas of your app. This lets users complete tasks quicker and extends the functional reach of the app to the home screen.</p>
+<p>Good candidates for navigation links to surface on widgets are:</p>
+<ul>
+  <li>Generative functions: These are the functions that allow the user to create new content for an app, such as creating a new document or a new message.</li>
+  <li>Open application at top level: Tapping on an information element will usually navigate the user to a lower level detail screen. Providing access to the top level of your application provides more navigation flexibility and can replace a dedicated app shortcut that users would otherwise use to navigate to the app from the home screen. Using your application icon as an affordance can also provide your widget with a clear identity in case the data you're displaying is ambiguous.</li>
+</ul>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-6">
+    <h3>Widget resizing</h3>
+    <p>With version 3.1, Android introduced resizable widgets to the platform. Resizing allows users to adjust the height and/or the width of a widget within the constraints of the home panel placement grid. You can decide if your widget is freely resizable or if it is constrained to horizontal or vertical size changes. You do not have to support resizing if your particular widget is inherently fixed-size.</p>
+    <p>Allowing users to resize widgets has important benefits:</p>
+    <ul>
+      <li>They can fine-tune how much information they want to see on each widget.</li>
+      <li>They can better influence the layout of widgets and shortcuts on their home panels.</li>
+    </ul>
+  </div>
+
+  <div class="layout-content-col span-7">
+    <img src="{@docRoot}design/media/widgets_resizing01.png">
+    <div class="figure-caption">
+      A long press and subsequent release sets resizable widgets into resize mode. Users can use the drag handles or the widget corners to set the desired size.
+    </div>
+  </div>
+</div>
+
+<p>Planning a resize strategy for your widget depends on the type of widget you're creating. List or grid-based collection widgets are usually straightforward because resizing the widget will simply expand or contract the vertical scrolling area. Regardless of the of the widget's size, the user can still scroll all information elements into view. Information widgets on the other hand require a bit more hands-on planning, since they are not scrollable and all content has to fit within a given size. You will have to dynamically adjust your widget's content and layout to the size the user defined through the resize operation.</p>
+<img src="{@docRoot}design/media/widgets_resizing02.png">
+<p>In this simple example the user can horizontally resize a weather widget in 4 steps and expose richer information about the weather at the current location as the widget grows.</p>
+<p>For each widget size determine how much of your app's information should surface. For smaller sizes concentrate on the essential and then add more contextual information as the widget grows horizontally and vertically.</p>
+
+<h3>Layout considerations</h3>
+<p>It will be tempting to layout your widgets according to the dimensions of the placement grid of a particular device that you own and develop with. This can be a useful initial approximation as you layout your widget, but keep the following in mind:</p>
+<ul>
+  <li>The number, size and spacing of cells can vary widely from device to device, and hence it is very important that your widget is flexible and can accommodate more or less space than anticipated.</li>
+  <li>In fact, as the user resizes a widget, the system will respond with a dp size range in which your widget can redraw itself. Planning your widget resizing strategy across "size buckets" rather than variable grid dimensions will give you the most reliable results.</li>
+</ul>
+
+<h3>Widget configuration</h3>
+<div class="layout-content-row">
+  <div class="layout-content-col span-6">
+    <p>Sometimes widgets need to be setup before they can become useful. Think of an email widget for example, where you need to provide an account before the inbox can be displayed. Or a static photo widget where the user has to assign the picture that is to be displayed from the gallery.</p>
+    <p>Android widgets display their configuration choices right after the widget is dropped onto a home panel. Keep the widget configuration light and don't present more than 2-3 configuration elements. Use dialog-style instead of full-screen activities to present configuration choices and retain the user's context of place, even if doing so requires use of multiple dialogs.</p>
+<p>Once setup, there typically is not a lot of reason to revisit the setup. Therefore Android widgets do not show a "Setup" or "Configuration" button.</p>
+  </div>
+
+  <div class="layout-content-col span-6">
+    <img src="{@docRoot}design/media/widgets_config.png">
+    <div class="figure-caption">
+      After adding a Play widget to a home panel, the widget asks the user to specify the type of media the widget should display.
+    </div>
+  </div>
+</div>
+
+<h3>Checklist</h3>
+<ul>
+  <li>Focus on small portions of glanceable information on your widget. Expand on the information in your app.</li>
+  <li>Choose the right widget type for your purpose.</li>
+  <li>For resizable widgets, plan how the content for your widget should adapt to different sizes.</li>
+  <li>Make your widget orientation and device independent by ensuring that the layout is capable of stretching and contracting.</li>
+</ul>
\ No newline at end of file
diff --git a/docs/html/design/style/color.jd b/docs/html/design/style/color.jd
index 9c7b6b6..5be34ac 100644
--- a/docs/html/design/style/color.jd
+++ b/docs/html/design/style/color.jd
@@ -115,7 +115,7 @@
 
 <p>Blue is the standard accent color in Android's color palette. Each color has a corresponding darker
 shade that can be used as a complement when needed.</p>
-<p><a href="https://dl-ssl.google.com/android/design/Android_Design_Color_Swatches_20120229.zip">Download the swatches</a></p>
+<p><a href="{@docRoot}downloads/design/Android_Design_Color_Swatches_20120229.zip">Download the swatches</a></p>
 
 <img src="{@docRoot}design/media/color_spectrum.png">
 
diff --git a/docs/html/design/style/iconography.jd b/docs/html/design/style/iconography.jd
index 775e45d..31274c5 100644
--- a/docs/html/design/style/iconography.jd
+++ b/docs/html/design/style/iconography.jd
@@ -110,7 +110,7 @@
 </p>
 <p>
 
-<a href="https://dl-ssl.google.com/android/design/Android_Design_Icons_20120229.zip">Download the Action Bar Icon Pack</a>
+<a href="{@docRoot}downloads/design/Android_Design_Icons_20120229.zip">Download the Action Bar Icon Pack</a>
 
 </p>
 
diff --git a/docs/html/design/style/typography.jd b/docs/html/design/style/typography.jd
index db2fb5f..a699bed 100644
--- a/docs/html/design/style/typography.jd
+++ b/docs/html/design/style/typography.jd
@@ -18,8 +18,8 @@
 
     <img src="{@docRoot}design/media/typography_alphas.png">
 
-<p><a href="https://dl-ssl.google.com/android/design/Roboto_Hinted_20111129.zip">Download Roboto</a></p>
-<p><a href="https://dl-ssl.google.com/android/design/Roboto_Specimen_Book_20111129.pdf">Specimen Book</a></p>
+<p><a href="{@docRoot}downloads/design/Roboto_Hinted_20111129.zip">Download Roboto</a></p>
+<p><a href="{@docRoot}downloads/design/Roboto_Specimen_Book_20111129.pdf">Specimen Book</a></p>
 
   </div>
 </div>
diff --git a/docs/html/design/videos/index.jd b/docs/html/design/videos/index.jd
new file mode 100644
index 0000000..272183f
--- /dev/null
+++ b/docs/html/design/videos/index.jd
@@ -0,0 +1,67 @@
+page.title=Videos
+@jd:body
+
+<p>The Android Design Team was pleased to present five fantastic design-oriented sessions at Google I/O 2012. Visit these pages to view the videos and presentations from the conference.</p>
+<img src="{@docRoot}design/media/extras_googleio_12.png">
+
+<div class="vspace size-2">&nbsp;</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-7">
+    <h3 id="design-for-success"><a href="https://developers.google.com/events/io/sessions/gooio2012/112/">Android Design for Success</a></h3>
+    <p>You have a great idea for an Android app. You want it to stand out among hundreds of thousands. You want your users to love it and tell everyone they know. The Android User Experience team is here to help. We talk about the Android Design guide and other tricks of the trade for creating apps that delight users and help them accomplish their goals. No design background is required.</p>
+  </div>
+  <div class="layout-content-col span-6">
+    <iframe width="355" height="200" src="http://www.youtube.com/embed/2NL_83EG0no" frameborder="0" allowfullscreen=""></iframe>
+  </div>
+</div>
+
+<div class="vspace size-2">&nbsp;</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-7">
+    <h3 id="design-for-engineers"><a href="https://developers.google.com/events/io/sessions/gooio2012/1204/">Android Design for Engineers</a></h3>
+    <p>Design isn't black magic, it's a field that people can learn. In this talk two elite designers from Google give you an advanced crash course in interactive and visual design. Topics include mental models, natural mappings, metaphors, mode errors, visual hierarchies, typography and gestalt principles. Correctly applied, this knowledge can drastically improve the quality of your work.</p>
+  </div>
+  <div class="layout-content-col span-6">
+    <iframe width="355" height="200" src="http://www.youtube.com/embed/iJDoxOTyMdk" frameborder="0" allowfullscreen=""></iframe>
+  </div>
+</div>
+
+<div class="vspace size-2">&nbsp;</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-7">
+    <h3 id="navigation-in-android"><a href="https://developers.google.com/events/io/sessions/gooio2012/114/">Navigation in Android</a></h3>
+    <p>An app is useless if people can't find their way around it. Android introduced big navigation-support changes in 3.0 and 4.0. The Action Bar offers a convenient control for Up navigation, the Back key's behavior became more consistent within tasks, and the Recent Tasks UI got an overhaul. In this talk, we discuss how and why we got where we are today, how to think about navigation when designing your app's user experience, and how to write apps that offer effortless navigation in multiple Android versions.</p>
+  </div>
+  <div class="layout-content-col span-6">
+    <iframe width="355" height="200" src="http://www.youtube.com/embed/XwGHJJYBs0Q" frameborder="0" allowfullscreen=""></iframe>
+  </div>
+</div>
+
+<div class="vspace size-2">&nbsp;</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-7">
+    <h3 id="now-what"><a href="https://developers.google.com/events/io/sessions/gooio2012/115/">So You've Read the Design Guide&#59; Now What?</a></h3>
+    <p>The Android Design Guide describes how to design beautiful Android apps, but not how to build them. In this talk we give practical tips for how to apply fit &amp; finish as you implement your design, we show you how to avoid some common pitfalls, we describe some useful patterns, and show how tools can help.</p>
+  </div>
+  <div class="layout-content-col span-6">
+    <iframe width="355" height="200" src="http://www.youtube.com/embed/2jCVmfCse1E" frameborder="0" allowfullscreen=""></iframe>
+  </div>
+</div>
+
+<div class="vspace size-2">&nbsp;</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-7">
+    <h3 id="playing-with-patterns"><a href="https://developers.google.com/events/io/sessions/gooio2012/131/">Playing with Patterns</a></h3>
+    <p>Best-in-class application designers and developers talk about their experience in developing for Android, showing screenshots from their app, exploring the challenges they faced, and offering creative solutions congruent with the Android Design guide. Guests are invited to show examples of visual and interaction patterns in their application that manage to keep it simultaneously consistent and personal.</p>
+  </div>
+  <div class="layout-content-col span-6">
+    <iframe width="355" height="200" src="http://www.youtube.com/embed/8iUbr8RZKtg" frameborder="0" allowfullscreen=""></iframe>
+  </div>
+</div>
+
+<p>Videos for the entire Design Track can also be found on the <a href="http://www.youtube.com/playlist?list=PL54FA004D676C3EE9">Android Developers Channel</a> on YouTube.</p>
diff --git a/docs/html/develop/index.jd b/docs/html/develop/index.jd
index eaa70e21d..14ab5d5 100644
--- a/docs/html/develop/index.jd
+++ b/docs/html/develop/index.jd
@@ -180,7 +180,6 @@
 </div>
 
 <br class="clearfix"/>
-    </div>
 
       
       
diff --git a/docs/html/guide/practices/security.jd b/docs/html/guide/practices/security.jd
index ce59a9d..36eeff8 100644
--- a/docs/html/guide/practices/security.jd
+++ b/docs/html/guide/practices/security.jd
@@ -1,11 +1,11 @@
-page.title=Designing for Security
+page.title=Designing for Security
 @jd:body
 
 <div id="qv-wrapper">
 <div id="qv">
 <h2>In this document</h2>
 <ol>
-<li><a href="#Dalvik">Using Davlik Code</a></li>
+<li><a href="#Dalvik">Using Dalvik Code</a></li>
 <li><a href="#Native">Using Native Code</a></li>
 <li><a href="#Data">Storing Data</a></li>
 <li><a href="#IPC">Using IPC</a></li>
diff --git a/docs/html/guide/topics/media/camera.jd b/docs/html/guide/topics/media/camera.jd
index a63270a..3fe23f8 100644
--- a/docs/html/guide/topics/media/camera.jd
+++ b/docs/html/guide/topics/media/camera.jd
@@ -617,7 +617,7 @@
 
         // Create our Preview view and set it as the content of our activity.
         mPreview = new CameraPreview(this, mCamera);
-        FrameLayout preview = (FrameLayout) findViewById(id.camera_preview);
+        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
         preview.addView(mPreview);
     }
 }
diff --git a/docs/html/guide/topics/providers/content-provider-basics.jd b/docs/html/guide/topics/providers/content-provider-basics.jd
index 8c47ad72..527e713 100644
--- a/docs/html/guide/topics/providers/content-provider-basics.jd
+++ b/docs/html/guide/topics/providers/content-provider-basics.jd
@@ -2,9 +2,7 @@
 @jd:body
 <div id="qv-wrapper">
 <div id="qv">
-
-
-                    <!-- In this document -->
+<!-- In this document -->
 <h2>In this document</h2>
 <ol>
     <li>
@@ -238,15 +236,11 @@
 </p>
 <p>
     For example, to get a list of the words and their locales from the User Dictionary Provider,
-    you call {@link android.content.ContentResolver#query(Uri, String[], String, String[], String)
-    ContentResolver.query()}.
-    The {@link android.content.ContentResolver#query(Uri, String[], String, String[], String)
-    query()} method calls the
-    {@link android.content.ContentProvider#query(Uri, String[], String, String[], String)
-    ContentProvider.query()} method defined by the User Dictionary Provider. The following lines
-    of code show a
-    {@link android.content.ContentResolver#query(Uri, String[], String, String[], String)
-    ContentResolver.query()} call:
+    you call {@link android.content.ContentResolver#query ContentResolver.query()}.
+    The {@link android.content.ContentResolver#query query()} method calls the
+    {@link android.content.ContentProvider#query ContentProvider.query()} method defined by the 
+    User Dictionary Provider. The following lines of code show a
+    {@link android.content.ContentResolver#query ContentResolver.query()} call:
 <p>
 <pre>
 // Queries the user dictionary and returns results
@@ -259,7 +253,7 @@
 </pre>
 <p>
     Table 2 shows how the arguments to
-    {@link android.content.ContentResolver#query(Uri, String[], String, String[], String)
+    {@link android.content.ContentResolver#query 
     query(Uri,projection,selection,selectionArgs,sortOrder)} match an SQL SELECT statement:
 </p>
 <p class="table-caption">
@@ -344,7 +338,7 @@
     <code>4</code> from user dictionary, you can use this content URI:
 </p>
 <pre>
-Uri singleUri = ContentUri.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
+Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
 </pre>
 <p>
     You often use id values when you've retrieved a set of rows and then want to update or delete
@@ -354,7 +348,7 @@
     <strong>Note:</strong> The {@link android.net.Uri} and {@link android.net.Uri.Builder} classes
     contain convenience methods for constructing well-formed Uri objects from strings. The
     {@link android.content.ContentUris} contains convenience methods for appending id values to
-    a URI. The previous snippet uses {@link android.content.ContentUris#withAppendedId(Uri, long)
+    a URI. The previous snippet uses {@link android.content.ContentUris#withAppendedId
     withAppendedId()} to append an id to the UserDictionary content URI.
 </p>
 
@@ -367,10 +361,9 @@
 </p>
 <p class="note">
     For the sake of clarity, the code snippets in this section call
-    {@link android.content.ContentResolver#query(Uri, String[], String, String[], String)
-    ContentResolver.query()} on the "UI thread"". In actual code, however, you should
-    do queries asynchronously on a separate thread. One way to do this is to use the
-    {@link android.content.CursorLoader} class, which is described
+    {@link android.content.ContentResolver#query ContentResolver.query()} on the "UI thread"". In 
+    actual code, however, you should do queries asynchronously on a separate thread. One way to do 
+    this is to use the {@link android.content.CursorLoader} class, which is described
     in more detail in the <a href="{@docRoot}guide/components/loaders.html">
     Loaders</a> guide. Also, the lines of code are snippets only; they don't show a complete
     application.
@@ -391,8 +384,8 @@
     To retrieve data from a provider, your application needs "read access permission" for the
     provider. You can't request this permission at run-time; instead, you have to specify that
     you need this permission in your manifest, using the
-    <code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">
-    &lt;uses-permission&gt;</a></code> element and the exact permission name defined by the
+<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">&lt;uses-permission&gt;</a></code>
+    element and the exact permission name defined by the
     provider. When you specify this element in your manifest, you are in effect "requesting" this
     permission for your application. When users install your application, they implicitly grant
     this request.
@@ -436,10 +429,9 @@
 </pre>
 <p>
     The next snippet shows how to use
-    {@link android.content.ContentResolver#query(Uri, String[], String, String[], String)
-    ContentResolver.query()}, using the User Dictionary Provider as an example.
-    A provider client query is similar to an SQL query, and it contains a set of columns to return,
-    a set of selection criteria, and a sort order.
+    {@link android.content.ContentResolver#query ContentResolver.query()}, using the User Dictionary
+    Provider as an example. A provider client query is similar to an SQL query, and it contains a 
+    set of columns to return, a set of selection criteria, and a sort order.
 </p>
 <p>
     The set of columns that the query should return is called a <strong>projection</strong>
@@ -448,9 +440,9 @@
 <p>
     The expression that specifies the rows to retrieve is split into a selection clause and
     selection arguments. The selection clause is a combination of logical and Boolean expressions,
-    column names, and values (the variable <code>mSelection</code>). If you specify the replaceable
-    parameter <code>?</code> instead of a value, the query method retrieves the value from the
-    selection arguments array (the variable <code>mSelectionArgs</code>).
+    column names, and values (the variable <code>mSelectionClause</code>). If you specify the 
+    replaceable parameter <code>?</code> instead of a value, the query method retrieves the value 
+    from the selection arguments array (the variable <code>mSelectionArgs</code>).
 </p>
 <p>
     In the next snippet, if the user doesn't enter a word, the selection clause is set to
@@ -517,7 +509,7 @@
     This query is analogous to the SQL statement:
 </p>
 <pre>
-SELECT _ID, word, frequency, locale FROM words WHERE word = &lt;userinput&gt; ORDER BY word ASC;
+SELECT _ID, word, locale FROM words WHERE word = &lt;userinput&gt; ORDER BY word ASC;
 </pre>
 <p>
     In this SQL statement, the actual column names are used instead of contract class constants.
@@ -575,16 +567,15 @@
 <!-- Displaying the results -->
 <h3 id="DisplayResults">Displaying query results</h3>
 <p>
-    The {@link android.content.ContentResolver#query(Uri, String[], String, String[], String)
-    ContentResolver.query()} client method always returns a {@link android.database.Cursor}
-    containing the columns specified by the query's projection for the rows that match the query's
-    selection criteria. A {@link android.database.Cursor} object provides random read access to the
-    rows and columns it contains. Using {@link android.database.Cursor} methods,
-    you can iterate over the rows in the results, determine the data type of each column, get the
-    data out of a column, and examine other properties of the results. Some
-    {@link android.database.Cursor} implementations automatically update the object when the
-    provider's data changes, or trigger methods in an observer object when the
-    {@link android.database.Cursor} changes, or both.
+    The {@link android.content.ContentResolver#query ContentResolver.query()} client method always 
+    returns a {@link android.database.Cursor} containing the columns specified by the query's 
+    projection for the rows that match the query's selection criteria. A 
+    {@link android.database.Cursor} object provides random read access to the rows and columns it 
+    contains. Using {@link android.database.Cursor} methods, you can iterate over the rows in the 
+    results, determine the data type of each column, get the data out of a column, and examine other
+    properties of the results. Some {@link android.database.Cursor} implementations automatically 
+    update the object when the provider's data changes, or trigger methods in an observer object 
+    when the {@link android.database.Cursor} changes, or both.
 </p>
 <p class="note">
     <strong>Note:</strong> A provider may restrict access to columns based on the nature of the
@@ -594,7 +585,7 @@
 <p>
     If no rows match the selection criteria, the provider
     returns a {@link android.database.Cursor} object for which
-    {@link android.database.Cursor#getCount() Cursor.getCount()} is 0 (an empty cursor).
+    {@link android.database.Cursor#getCount Cursor.getCount()} is 0 (an empty cursor).
 </p>
 <p>
     If an internal error occurs, the results of the query depend on the particular provider. It may
@@ -685,8 +676,8 @@
 <p>
     {@link android.database.Cursor} implementations contain several "get" methods for
     retrieving different types of data from the object. For example, the previous snippet
-    uses {@link android.database.Cursor#getString(int) getString()}. They also have a
-    {@link android.database.Cursor#getType(int) getType()} method that returns a value indicating
+    uses {@link android.database.Cursor#getString getString()}. They also have a
+    {@link android.database.Cursor#getType getType()} method that returns a value indicating
     the data type of the column.
 </p>
 
@@ -713,17 +704,16 @@
 </p>
 <p>
     To get the permissions needed to access a provider, an application requests them with a
-    <code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">
-    &lt;uses-permission&gt;</a></code> element in its manifest file.
-    When the Android Package Manager installs the application, a user must approve all of the
-    permissions the application requests. If the user approves all of them, Package Manager
-    continues the installation; if the user doesn't approve them, Package Manager
+<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">&lt;uses-permission&gt;</a></code>
+    element in its manifest file. When the Android Package Manager installs the application, a user 
+    must approve all of the permissions the application requests. If the user approves all of them, 
+    Package Manager continues the installation; if the user doesn't approve them, Package Manager
     aborts the installation.
 </p>
 <p>
     The following
-    <code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">
-    &lt;uses-permission&gt;</a></code> element requests read access to the User Dictionary Provider:
+<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">&lt;uses-permission&gt;</a></code> 
+    element requests read access to the User Dictionary Provider:
 </p>
 <pre>
     &lt;uses-permission android:name="android.permission.READ_USER_DICTIONARY"&gt;
@@ -746,7 +736,7 @@
 <h3 id="Inserting">Inserting data</h3>
 <p>
     To insert data into a provider, you call the
-    {@link android.content.ContentResolver#insert(Uri,ContentValues) ContentResolver.insert()}
+    {@link android.content.ContentResolver#insert ContentResolver.insert()}
     method. This method inserts a new row into the provider and returns a content URI for that row.
     This snippet shows how to insert a new word into the User Dictionary Provider:
 </p>
@@ -777,8 +767,7 @@
     The data for the new row goes into a single {@link android.content.ContentValues} object, which
     is similar in form to a one-row cursor. The columns in this object don't need to have the
     same data type, and if you don't want to specify a value at all, you can set a column
-    to <code>null</code> using {@link android.content.ContentValues#putNull(String)
-    ContentValues.putNull()}.
+    to <code>null</code> using {@link android.content.ContentValues#putNull ContentValues.putNull()}.
 </p>
 <p>
     The snippet doesn't add the <code>_ID</code> column, because this column is maintained
@@ -799,17 +788,16 @@
 </p>
 <p>
     To get the value of <code>_ID</code> from the returned {@link android.net.Uri}, call
-    {@link android.content.ContentUris#parseId(Uri) ContentUris.parseId()}.
+    {@link android.content.ContentUris#parseId ContentUris.parseId()}.
 </p>
 <h3 id="Updating">Updating data</h3>
 <p>
     To update a row, you use a {@link android.content.ContentValues} object with the updated
     values just as you do with an insertion, and selection criteria just as you do with a query.
     The client method you use is
-    {@link android.content.ContentResolver#update(Uri, ContentValues, String, String[])
-    ContentResolver.update()}. You only need to add values to the
-    {@link android.content.ContentValues} object for columns you're updating. If you want to clear
-    the contents of a column, set the value to <code>null</code>.
+    {@link android.content.ContentResolver#update ContentResolver.update()}. You only need to add 
+    values to the {@link android.content.ContentValues} object for columns you're updating. If you 
+    want to clear the contents of a column, set the value to <code>null</code>.
 </p>
 <p>
     The following snippet changes all the rows whose locale has the language "en" to a
@@ -842,9 +830,8 @@
 </pre>
 <p>
     You should also sanitize user input when you call
-    {@link android.content.ContentResolver#update(Uri, ContentValues, String, String[])
-    ContentResolver.update()}. To learn more about this, read the section
-    <a href="#Injection">Protecting against malicious input</a>.
+    {@link android.content.ContentResolver#update ContentResolver.update()}. To learn more about 
+    this, read the section <a href="#Injection">Protecting against malicious input</a>.
 </p>
 <h3 id="Deleting">Deleting data</h3>
 <p>
@@ -873,9 +860,8 @@
 </pre>
 <p>
     You should also sanitize user input when you call
-    {@link android.content.ContentResolver#delete(Uri, String, String[])
-    ContentResolver.delete()}. To learn more about this, read the section
-    <a href="#Injection">Protecting against malicious input</a>.
+    {@link android.content.ContentResolver#delete ContentResolver.delete()}. To learn more about 
+    this, read the section <a href="#Injection">Protecting against malicious input</a>.
 </p>
 <!-- Provider Data Types -->
 <h2 id="DataTypes">Provider Data Types</h2>
@@ -907,7 +893,7 @@
     The data types for the User Dictionary Provider are listed in the reference documentation
     for its contract class {@link android.provider.UserDictionary.Words} (contract classes are
     described in the section <a href="#ContractClasses">Contract Classes</a>).
-    You can also determine the data type by calling {@link android.database.Cursor#getType(int)
+    You can also determine the data type by calling {@link android.database.Cursor#getType
     Cursor.getType()}.
 </p>
 <p>
@@ -918,7 +904,7 @@
     data structures or files. For example, the {@link android.provider.ContactsContract.Data}
     table in the Contacts Provider uses MIME types to label the type of contact data stored in each
     row. To get the MIME type corresponding to a content URI, call
-    {@link android.content.ContentResolver#getType(Uri) ContentResolver.getType()}.
+    {@link android.content.ContentResolver#getType ContentResolver.getType()}.
 </p>
 <p>
     The section <a href="#MIMETypeReference">MIME Type Reference</a> describes the
@@ -935,8 +921,7 @@
     <li>
         <a href="#Batch">Batch access</a>: You can create a batch of access calls with methods in
         the {@link android.content.ContentProviderOperation} class, and then apply them with
-        {@link android.content.ContentResolver#applyBatch(String, ArrayList)
-        ContentResolver.applyBatch()}.
+        {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()}.
     </li>
     <li>
         Asynchronous queries: You should do queries in a separate thread. One way to do this is to
@@ -963,11 +948,10 @@
     To access a provider in "batch mode",
     you create an array of {@link android.content.ContentProviderOperation} objects and then
     dispatch them to a content provider with
-    {@link android.content.ContentResolver#applyBatch(String, ArrayList)
-    ContentResolver.applyBatch()}. You pass the content provider's <em>authority</em> to this
-    method, rather than a particular content URI, which allows each
-    {@link android.content.ContentProviderOperation} object in the array to work against a
-    different table. A call to {@link android.content.ContentResolver#applyBatch(String, ArrayList)
+    {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()}. You pass the 
+    content provider's <em>authority</em> to this  method, rather than a particular content URI. 
+    This allows each {@link android.content.ContentProviderOperation} object in the array to work 
+    against a different table. A call to {@link android.content.ContentResolver#applyBatch
     ContentResolver.applyBatch()} returns an array of results.
 </p>
 <p>
@@ -1028,14 +1012,13 @@
 </p>
 <p>
     A provider defines URI permissions for content URIs in its manifest, using the
-    <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn">
-    android:grantUriPermission</a></code>
-    attribute of the <a href="{@docRoot}guide/topics/manifest/provider-element.html">
-    {@code &lt;provider&gt;}</a>
+<code><a href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn">android:grantUriPermission</a></code>
+    attribute of the 
+<code><a href="{@docRoot}guide/topics/manifest/provider-element.html">&lt;provider&gt;</a></code>
     element, as well as the
-    <a href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html">{@code 
-    &lt;grant-uri-permission&gt;}</a> child element of the
-    <a href="{@docRoot}guide/topics/manifest/provider-element.html">{@code &lt;provider&gt;}</a>
+<code><a href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html">&lt;grant-uri-permission&gt;</a></code>
+    child element of the
+<code><a href="{@docRoot}guide/topics/manifest/provider-element.html">&lt;provider&gt;</a></code>
     element. The URI permissions mechanism is explained in more detail in the
     <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> guide,
     in the section "URI Permissions".
@@ -1053,7 +1036,7 @@
         Your application sends an intent containing the action
         {@link android.content.Intent#ACTION_PICK} and the "contacts" MIME type
         {@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE}, using the
-        method {@link android.app.Activity#startActivityForResult(Intent, int)
+        method {@link android.app.Activity#startActivityForResult
         startActivityForResult()}.
     </li>
     <li>
@@ -1063,7 +1046,7 @@
     <li>
         In the selection activity, the user selects a
         contact to update. When this happens, the selection activity calls
-        {@link android.app.Activity#setResult(int, Intent) setResult(resultcode, intent)}
+        {@link android.app.Activity#setResult setResult(resultcode, intent)}
         to set up a intent to give back to your application. The intent contains the content URI
         of the contact the user selected, and the "extras" flags
         {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}. These flags grant URI
@@ -1073,7 +1056,7 @@
     </li>
     <li>
         Your activity returns to the foreground, and the system calls your activity's
-        {@link android.app.Activity#onActivityResult(int, int, Intent) onActivityResult()}
+        {@link android.app.Activity#onActivityResult onActivityResult()}
         method. This method receives the result intent created by the selection activity in
         the People app.
     </li>
diff --git a/docs/html/guide/topics/search/index.jd b/docs/html/guide/topics/search/index.jd
index 2ee624b..680c607 100644
--- a/docs/html/guide/topics/search/index.jd
+++ b/docs/html/guide/topics/search/index.jd
@@ -54,9 +54,9 @@
 if your data is stored in an SQLite database, you should use the {@link android.database.sqlite}
 APIs to perform searches.
 <br/><br/>
-Also, there is no guarantee that every device provides a dedicated SEARCH button to invoke the
+Also, there is no guarantee that a device provides a dedicated SEARCH button that invokes the
 search interface in your application. When using the search dialog or a custom interface, you
-must always provide a search button in your UI that activates the search interface. For more
+must provide a search button in your UI that activates the search interface. For more
 information, see <a href="search-dialog.html#InvokingTheSearchDialog">Invoking the search
 dialog</a>.</p>
 
diff --git a/docs/html/guide/topics/search/search-dialog.jd b/docs/html/guide/topics/search/search-dialog.jd
index 49451ac..b9a26d6 100644
--- a/docs/html/guide/topics/search/search-dialog.jd
+++ b/docs/html/guide/topics/search/search-dialog.jd
@@ -6,14 +6,6 @@
 <div id="qv-wrapper">
 <div id="qv">
 
-  <h2>Quickview</h2>
-  <ul>
-    <li>The Android system sends search queries from the search dialog or widget to an activity you
-specify to perform searches and present results</li>
-    <li>You can put the search widget in the Action Bar, as an "action view," for quick
-access</li>
-  </ul>
-
 
 <h2>In this document</h2>
 <ol>
@@ -61,14 +53,8 @@
 
 <h2>Downloads</h2>
 <ol>
-<li><a href="{@docRoot}shareables/search_icons.zip">search_icons.zip</a></li>
-</ol>
-
-<h2>See also</h2>
-<ol>
-<li><a href="adding-recent-query-suggestions.html">Adding Recent Query Suggestions</a></li>
-<li><a href="adding-custom-suggestions.html">Adding Custom Suggestions</a></li>
-<li><a href="searchable-config.html">Searchable Configuration</a></li>
+<li><a href="{@docRoot}design/downloads/index.html#action-bar-icon-pack">Action Bar
+Icon Pack</a></li>
 </ol>
 
 </div>
@@ -142,12 +128,14 @@
   <li>A search interface, provided by either:
     <ul>
       <li>The search dialog
-        <p>By default, the search dialog is hidden, but appears at the top of the screen when the 
-user presses the device SEARCH button (when available) or another button in your user interface.</p>
+        <p>By default, the search dialog is hidden, but appears at the top of the screen when
+          you call {@link android.app.Activity#onSearchRequested()} (when the user presses your
+          Search button).</p>
       </li>
       <li>Or, a {@link android.widget.SearchView} widget
         <p>Using the search widget allows you to put the search box anywhere in your activity.
-Instead of putting it in your activity layout, however, it's usually more convenient for users as an
+Instead of putting it in your activity layout, you should usually use
+{@link android.widget.SearchView} as an 
 <a href="{@docRoot}guide/topics/ui/actionbar.html#ActionView">action view in the Action Bar</a>.</p>
       </li>
     </ul>
@@ -415,10 +403,9 @@
 your application for devices running Android 3.0, you should consider using the search widget
 instead (see the side box).</p>
 
-<p>The search dialog is always hidden by default, until the user activates it. If the user's device
-includes a SEARCH button, pressing it will activate the search dialog by default. Your application
-can also activate the search dialog on demand by calling {@link
-android.app.Activity#onSearchRequested onSearchRequested()}. However, neither of these work
+<p>The search dialog is always hidden by default, until the user activates it. Your application
+can activate the search dialog by calling {@link
+android.app.Activity#onSearchRequested onSearchRequested()}. However, this method doesn't work
 until you enable the search dialog for the activity.</p>
 
 <p>To enable the search dialog, you must indicate to the system which searchable activity should
@@ -469,8 +456,8 @@
 href="{@docRoot}guide/topics/manifest/meta-data-element.html">{@code &lt;meta-data&gt;}</a>
 element to declare which searchable activity to use for searches, the activity has enabled the
 search dialog.
-While the user is in this activity, the device SEARCH button (if available) and the {@link
-android.app.Activity#onSearchRequested onSearchRequested()} method will activate the search dialog.
+While the user is in this activity, the {@link
+android.app.Activity#onSearchRequested onSearchRequested()} method activates the search dialog.
 When the user executes the search, the system starts {@code SearchableActivity} and delivers it
 the {@link android.content.Intent#ACTION_SEARCH} intent.</p>
 
@@ -495,21 +482,22 @@
 
 <h3 id="InvokingTheSearchDialog">Invoking the search dialog</h3>
 
-<p>As mentioned above, the device SEARCH button will open the search dialog as long as the current
-activity has declared in the manifest the searchable activity to use.</p>
+<p>Although some devices provide a dedicated Search button, the behavior of the button may vary
+between devices and many devices do not provide a Search button at all. So when using the search
+dialog, you <strong>must provide a search button in your UI</strong> that activates the search
+dialog by calling {@link android.app.Activity#onSearchRequested()}.</p>
 
-<p>However, some devices do not include a dedicated SEARCH button, so you should not assume that
-it's always available. When using the search dialog, you must <strong>always provide another search
-button in your UI</strong> that activates the search dialog by calling {@link
-android.app.Activity#onSearchRequested()}.</p>
+<p>For instance, you should add a Search button in your <a
+href="{@docRoot}guide/topics/ui/menus.html#options-menu">Options Menu</a> or UI
+layout that calls {@link android.app.Activity#onSearchRequested()}. For consistency with
+the Android system and other apps, you should label your button with the Android Search icon that's
+available from the <a href="{@docRoot}design/downloads/index.html#action-bar-icon-pack">Action Bar
+Icon Pack</a>.</p>
 
-<p>For instance, you should either provide a menu item in your <a
-href="{@docRoot}guide/topics/ui/menus.html#options-menu">Options Menu</a> or a button in your
-activity layout that
-activates search by calling {@link android.app.Activity#onSearchRequested()}. The <a
-href="{@docRoot}shareables/search_icons.zip">search_icons.zip</a> file includes icons for
-medium and high density screens, which you can use for your search menu item or button (low-density
-screens scale-down the hdpi image by one half). </p>
+<p class="note"><strong>Note:</strong> If your app uses the <a
+href="{@docRoot}guide/topics/ui/actionbar.html">action bar</a>, then you should not use
+the search dialog for your search interface. Instead, use the <a href="#UsingSearchWidget">search
+widget</a> as a collapsible view in the action bar.</p>
 
 <p>You can also enable "type-to-search" functionality, which activates the search dialog when the
 user starts typing on the keyboard&mdash;the keystrokes are inserted into the search dialog. You can
diff --git a/docs/html/guide/topics/ui/controls/radiobutton.jd b/docs/html/guide/topics/ui/controls/radiobutton.jd
index f6f6d49..c96e576 100644
--- a/docs/html/guide/topics/ui/controls/radiobutton.jd
+++ b/docs/html/guide/topics/ui/controls/radiobutton.jd
@@ -72,7 +72,7 @@
 <pre>
 public void onRadioButtonClicked(View view) {
     // Is the button now checked?
-    boolean checked = (RadioButton) view).isChecked();
+    boolean checked = ((RadioButton) view).isChecked();
     
     // Check which radio button was clicked
     switch(view.getId()) {
diff --git a/docs/html/guide/topics/ui/declaring-layout.jd b/docs/html/guide/topics/ui/declaring-layout.jd
index e971a75..e229f23 100644
--- a/docs/html/guide/topics/ui/declaring-layout.jd
+++ b/docs/html/guide/topics/ui/declaring-layout.jd
@@ -32,12 +32,17 @@
     <li>{@link android.view.ViewGroup}</li>
     <li>{@link android.view.ViewGroup.LayoutParams}</li>
   </ol>
-</div>
+  
+  <h2>See also</h2>
+  <ol>
+    <li><a href="{@docRoot}training/basics/firstapp/building-ui.html">Building a Simple User
+Interface</a></li> </div>
 </div>
 
-<p>Your layout is the architecture for the user interface in an Activity.
-It defines the layout structure and holds all the elements that appear to the user. 
-You can declare your layout in two ways:</p>
+<p>A layout defines the visual structure for a user interface, such as the UI for an <a
+href="{@docRoot}guide/components/activities.html">activity</a> or <a
+href="{@docRoot}guide/topics/appwidgets/index.html">app widget</a>.
+You can declare a layout in two ways:</p>
 <ul>
 <li><strong>Declare UI elements in XML</strong>. Android provides a straightforward XML 
 vocabulary that corresponds to the View classes and subclasses, such as those for widgets and layouts.</li>
@@ -77,16 +82,6 @@
 
 <h2 id="write">Write the XML</h2>
 
-<div class="sidebox-wrapper">
-<div class="sidebox">
-<p>For your convenience, the API reference documentation for UI related classes
-lists the available XML attributes that correspond to the class methods, including inherited
-attributes.</p>
-<p>To learn more about the available XML elements and attributes, as well as the format of the XML file, see <a
-href="{@docRoot}guide/topics/resources/available-resources.html#layoutresources">Layout Resources</a>.</p>
-</div>
-</div>
-
 <p>Using Android's XML vocabulary, you can quickly design UI layouts and the screen elements they contain, in the same way you create web pages in HTML &mdash; with a series of nested elements. </p>
 
 <p>Each layout file must contain exactly one root element, which must be a View or ViewGroup object. Once you've defined the root element, you can add additional layout objects or widgets as child elements to gradually build a View hierarchy that defines your layout. For example, here's an XML layout that uses a vertical {@link android.widget.LinearLayout}
@@ -111,7 +106,8 @@
 <p>After you've declared your layout in XML, save the file with the <code>.xml</code> extension, 
 in your Android project's <code>res/layout/</code> directory, so it will properly compile. </p>
 
-<p>We'll discuss each of the attributes shown here a little later.</p>
+<p>More information about the syntax for a layout XML file is available in the <a
+href="{@docRoot}guide/topics/resources/layout-resource.html">Layout Resources</a> document.</p>
 
 <h2 id="load">Load the XML Resource</h2>
 
diff --git a/docs/html/guide/topics/ui/layout/gridview.jd b/docs/html/guide/topics/ui/layout/gridview.jd
index 11c5474..67bdd0f0 100644
--- a/docs/html/guide/topics/ui/layout/gridview.jd
+++ b/docs/html/guide/topics/ui/layout/gridview.jd
@@ -22,10 +22,15 @@
 scrollable grid. The grid items are automatically inserted to the layout using a {@link
 android.widget.ListAdapter}.</p>
 
+<p>For an introduction to how you can dynamically insert views using an adapter, read
+<a href="{@docRoot}guide/topics/ui/declaring-layout.html#AdapterViews">Building Layouts with
+  an Adapter</a>.</p>
+
 <img src="{@docRoot}images/ui/gridview.png" alt="" />
 
 
 <h2 id="example">Example</h2>
+
 <p>In this tutorial, you'll create a grid of image thumbnails. When an item is selected, a
 toast message will display the position of the image.</p>
 
diff --git a/docs/html/guide/topics/ui/layout/listview.jd b/docs/html/guide/topics/ui/layout/listview.jd
index 26a7597..fee5292 100644
--- a/docs/html/guide/topics/ui/layout/listview.jd
+++ b/docs/html/guide/topics/ui/layout/listview.jd
@@ -28,6 +28,10 @@
 android.widget.Adapter} that pulls content from a source such as an array or database query and
 converts each item result into a view that's placed into the list.</p>
 
+<p>For an introduction to how you can dynamically insert views using an adapter, read
+<a href="{@docRoot}guide/topics/ui/declaring-layout.html#AdapterViews">Building Layouts with
+  an Adapter</a>.</p>
+
 <img src="{@docRoot}images/ui/listview.png" alt="" />
 
 <h2 id="Loader">Using a Loader</h2>
@@ -147,5 +151,5 @@
 Provider</a>, if you want to
 try this code, your app must request the {@link android.Manifest.permission#READ_CONTACTS}
 permission in the manifest file:<br/>
-<code>&lt;uses-permission android:name="android.permission.READ_CONTACTS" /></p>
+<code>&lt;uses-permission android:name="android.permission.READ_CONTACTS" /></code></p>
 
diff --git a/docs/html/guide/topics/ui/settings.jd b/docs/html/guide/topics/ui/settings.jd
index fd3b684..33e164b 100644
--- a/docs/html/guide/topics/ui/settings.jd
+++ b/docs/html/guide/topics/ui/settings.jd
@@ -217,7 +217,7 @@
         android:dialogTitle="@string/pref_syncConnectionType"
         android:entries="@array/pref_syncConnectionTypes_entries"
         android:entryValues="@array/pref_syncConnectionTypes_values"
-        android:defaultValue="@string/pref_syncConnectionTypes_default" >
+        android:defaultValue="@string/pref_syncConnectionTypes_default" />
 &lt;/PreferenceScreen>
 </pre>
 
diff --git a/docs/html/training/basics/activity-lifecycle/starting.jd b/docs/html/training/basics/activity-lifecycle/starting.jd
index 1a4bc2d..dd17304 100644
--- a/docs/html/training/basics/activity-lifecycle/starting.jd
+++ b/docs/html/training/basics/activity-lifecycle/starting.jd
@@ -112,7 +112,7 @@
 </table>
 -->
 
-<p>As you'll learn in the following lessons, there are several situtations in which an activity
+<p>As you'll learn in the following lessons, there are several situations in which an activity
 transitions between different states that are illustrated in figure 1. However, only three of
 these states can be static. That is, the activity can exist in one of only three states for an
 extended period of time:</p>
diff --git a/docs/html/training/basics/firstapp/starting-activity.jd b/docs/html/training/basics/firstapp/starting-activity.jd
index 4d0a84a..3dafcfa 100644
--- a/docs/html/training/basics/firstapp/starting-activity.jd
+++ b/docs/html/training/basics/firstapp/starting-activity.jd
@@ -285,8 +285,8 @@
 
 <p>The app is now runnable because the {@link android.content.Intent} in the
 first activity now resolves to the {@code DisplayMessageActivity} class. If you run the app now,
-clicking the Send button starts the
-second activity, but it doesn't show anything yet.</p>
+clicking the Send button starts the second activity, but it's still using the default
+"Hello world" layout.</p>
 
 
 <h2 id="ReceiveIntent">Receive the Intent</h2>
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 2032f67..af8b0c2 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -561,6 +561,12 @@
         return rsnScriptCCreate(mContext, resName, cacheDir, script, length);
     }
 
+    native int  rsnScriptIntrinsicCreate(int con, int id, int eid);
+    synchronized int nScriptIntrinsicCreate(int id, int eid) {
+        validate();
+        return rsnScriptIntrinsicCreate(mContext, id, eid);
+    }
+
     native int  rsnSamplerCreate(int con, int magFilter, int minFilter,
                                  int wrapS, int wrapT, int wrapR, float aniso);
     synchronized int nSamplerCreate(int magFilter, int minFilter,
diff --git a/graphics/java/android/renderscript/ScriptIntrinsic.java b/graphics/java/android/renderscript/ScriptIntrinsic.java
new file mode 100644
index 0000000..6ad1527
--- /dev/null
+++ b/graphics/java/android/renderscript/ScriptIntrinsic.java
@@ -0,0 +1,35 @@
+/*
+ * 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 License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+
+
+/**
+ * @hide
+ **/
+public class ScriptIntrinsic extends Script {
+    ScriptIntrinsic(int id, RenderScript rs) {
+        super(id, rs);
+    }
+
+    public void forEach(Allocation ain, Allocation aout) {
+        forEach(0, ain, aout, null);
+    }
+}
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java b/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
new file mode 100644
index 0000000..0ae1449
--- /dev/null
+++ b/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
@@ -0,0 +1,65 @@
+/*
+ * 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 android.renderscript;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map.Entry;
+import java.util.HashMap;
+
+
+/**
+ * @hide
+ **/
+public class ScriptIntrinsicConvolve3x3 extends ScriptIntrinsic {
+    private float[] mValues = new float[9];
+
+    ScriptIntrinsicConvolve3x3(int id, RenderScript rs) {
+        super(id, rs);
+    }
+
+    /**
+     * Supported elements types are float, float4, uchar, uchar4
+     *
+     *
+     * @param rs
+     * @param e
+     *
+     * @return ScriptIntrinsicConvolve3x3
+     */
+    public static ScriptIntrinsicConvolve3x3 create(RenderScript rs, Element e) {
+        int id = rs.nScriptIntrinsicCreate(1, e.getID(rs));
+        return new ScriptIntrinsicConvolve3x3(id, rs);
+
+    }
+
+
+    public void setValues(float v[]) {
+        FieldPacker fp = new FieldPacker(9*4);
+        for (int ct=0; ct < mValues.length; ct++) {
+            mValues[ct] = v[ct];
+            fp.addF32(mValues[ct]);
+        }
+        setVar(0, fp);
+    }
+}
+
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicYuvToRGB.java b/graphics/java/android/renderscript/ScriptIntrinsicYuvToRGB.java
new file mode 100644
index 0000000..ee5f938
--- /dev/null
+++ b/graphics/java/android/renderscript/ScriptIntrinsicYuvToRGB.java
@@ -0,0 +1,64 @@
+/*
+ * 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 License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map.Entry;
+import java.util.HashMap;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+/**
+ * @hide
+ **/
+public class ScriptIntrinsicYuvToRGB extends ScriptIntrinsic {
+    ScriptIntrinsicYuvToRGB(int id, RenderScript rs) {
+        super(id, rs);
+    }
+
+
+
+    public static class Builder {
+        RenderScript mRS;
+
+        public Builder(RenderScript rs) {
+            mRS = rs;
+        }
+
+        public void setInputFormat(int inputFormat) {
+
+        }
+
+        public void setOutputFormat(Element e) {
+
+        }
+
+        public ScriptIntrinsicYuvToRGB create() {
+            return null;
+
+        }
+
+    }
+
+}
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 09f6952..a073c1a 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -1071,6 +1071,13 @@
     return ret;
 }
 
+static jint
+nScriptIntrinsicCreate(JNIEnv *_env, jobject _this, RsContext con, jint id, jint eid)
+{
+    LOG_API("nScriptIntrinsicCreate, con(%p) id(%i) element(%p)", con, id, (void *)eid);
+    return (jint)rsScriptIntrinsicCreate(con, id, (RsElement)eid);
+}
+
 // ---------------------------------------------------------------------------
 
 static jint
@@ -1412,6 +1419,7 @@
 {"rsnScriptSetVarObj",               "(IIII)V",                               (void*)nScriptSetVarObj },
 
 {"rsnScriptCCreate",                 "(ILjava/lang/String;Ljava/lang/String;[BI)I",  (void*)nScriptCCreate },
+{"rsnScriptIntrinsicCreate",         "(III)I",                                (void*)nScriptIntrinsicCreate },
 
 {"rsnProgramStoreCreate",            "(IZZZZZZIII)I",                         (void*)nProgramStoreCreate },
 
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 95fc2c5..2de70d462 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -1699,7 +1699,9 @@
     addFloat(hOffset);
     addFloat(vOffset);
     paint->setAntiAlias(true);
-    addPaint(paint);
+    SkPaint* addedPaint = addPaint(paint);
+    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint);
+    fontRenderer.precache(addedPaint, text, count);
     return DrawGlInfo::kStatusDone;
 }
 
@@ -1711,7 +1713,9 @@
     addInt(count);
     addFloats(positions, count * 2);
     paint->setAntiAlias(true);
-    addPaint(paint);
+    SkPaint* addedPaint = addPaint(paint);
+    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint);
+    fontRenderer.precache(addedPaint, text, count);
     return DrawGlInfo::kStatusDone;
 }
 
@@ -1742,7 +1746,11 @@
     addFloat(x);
     addFloat(y);
     addFloats(positions, count * 2);
-    addPaint(paint);
+    SkPaint* addedPaint = addPaint(paint);
+    if (!reject) {
+        FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint);
+        fontRenderer.precache(addedPaint, text, count);
+    }
     addFloat(length);
     addSkip(location);
     return DrawGlInfo::kStatusDone;
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 60a40c6..c8b3e47 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -770,10 +770,10 @@
         addInt((int) pathCopy);
     }
 
-    inline void addPaint(SkPaint* paint) {
+    inline SkPaint* addPaint(SkPaint* paint) {
         if (!paint) {
             addInt((int) NULL);
-            return;
+            return paint;
         }
 
         SkPaint* paintCopy = mPaintMap.valueFor(paint);
@@ -785,6 +785,8 @@
         }
 
         addInt((int) paintCopy);
+
+        return paintCopy;
     }
 
     inline void addDisplayList(DisplayList* displayList) {
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index ccddd91..caeeb87 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -37,27 +37,153 @@
 #define DEFAULT_TEXT_CACHE_WIDTH 1024
 #define DEFAULT_TEXT_CACHE_HEIGHT 256
 #define MAX_TEXT_CACHE_WIDTH 2048
-#define TEXTURE_BORDER_SIZE 1
+#define CACHE_BLOCK_ROUNDING_SIZE 4
 
 #define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
 
 ///////////////////////////////////////////////////////////////////////////////
-// CacheTextureLine
+// CacheBlock
 ///////////////////////////////////////////////////////////////////////////////
 
-bool CacheTextureLine::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
-    if (glyph.fHeight + TEXTURE_BORDER_SIZE > mMaxHeight) {
+/**
+ * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width
+ * order, except for the final block (the remainder space at the right, since we fill from the
+ * left).
+ */
+CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock *newBlock) {
+#if DEBUG_FONT_RENDERER
+    ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
+            newBlock, newBlock->mX, newBlock->mY,
+            newBlock->mWidth, newBlock->mHeight);
+#endif
+    CacheBlock *currBlock = head;
+    CacheBlock *prevBlock = NULL;
+    while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
+        if (newBlock->mWidth < currBlock->mWidth) {
+            newBlock->mNext = currBlock;
+            newBlock->mPrev = prevBlock;
+            currBlock->mPrev = newBlock;
+            if (prevBlock) {
+                prevBlock->mNext = newBlock;
+                return head;
+            } else {
+                return newBlock;
+            }
+        }
+        prevBlock = currBlock;
+        currBlock = currBlock->mNext;
+    }
+    // new block larger than all others - insert at end (but before the remainder space, if there)
+    newBlock->mNext = currBlock;
+    newBlock->mPrev = prevBlock;
+    if (currBlock) {
+        currBlock->mPrev = newBlock;
+    }
+    if (prevBlock) {
+        prevBlock->mNext = newBlock;
+        return head;
+    } else {
+        return newBlock;
+    }
+}
+
+CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock *blockToRemove) {
+#if DEBUG_FONT_RENDERER
+    ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
+            blockToRemove, blockToRemove->mX, blockToRemove->mY,
+            blockToRemove->mWidth, blockToRemove->mHeight);
+#endif
+    CacheBlock* newHead = head;
+    CacheBlock* nextBlock = blockToRemove->mNext;
+    CacheBlock* prevBlock = blockToRemove->mPrev;
+    if (prevBlock) {
+        prevBlock->mNext = nextBlock;
+    } else {
+        newHead = nextBlock;
+    }
+    if (nextBlock) {
+        nextBlock->mPrev = prevBlock;
+    }
+    delete blockToRemove;
+    return newHead;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CacheTexture
+///////////////////////////////////////////////////////////////////////////////
+
+bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
+    if (glyph.fHeight + TEXTURE_BORDER_SIZE > mHeight) {
         return false;
     }
 
-    if (mCurrentCol + glyph.fWidth + TEXTURE_BORDER_SIZE * 2 < mMaxWidth) {
-        *retOriginX = mCurrentCol + TEXTURE_BORDER_SIZE;
-        *retOriginY = mCurrentRow + TEXTURE_BORDER_SIZE;
-        mCurrentCol += glyph.fWidth + TEXTURE_BORDER_SIZE * 2;
-        mDirty = true;
-        return true;
+    uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE;
+    uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE;
+    // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE.
+    // This columns for glyphs that are close but not necessarily exactly the same size. It trades
+    // off the loss of a few pixels for some glyphs against the ability to store more glyphs
+    // of varying sizes in one block.
+    uint16_t roundedUpW =
+            (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE;
+    CacheBlock *cacheBlock = mCacheBlocks;
+    while (cacheBlock) {
+        // Store glyph in this block iff: it fits the block's remaining space and:
+        // it's the remainder space (mY == 0) or there's only enough height for this one glyph
+        // or it's within ROUNDING_SIZE of the block width
+        if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight &&
+                (cacheBlock->mY == TEXTURE_BORDER_SIZE ||
+                        (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) {
+            if (cacheBlock->mHeight - glyphH < glyphH) {
+                // Only enough space for this glyph - don't bother rounding up the width
+                roundedUpW = glyphW;
+            }
+            *retOriginX = cacheBlock->mX;
+            *retOriginY = cacheBlock->mY;
+            // If this is the remainder space, create a new cache block for this column. Otherwise,
+            // adjust the info about this column.
+            if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
+                uint16_t oldX = cacheBlock->mX;
+                // Adjust remainder space dimensions
+                cacheBlock->mWidth -= roundedUpW;
+                cacheBlock->mX += roundedUpW;
+                if (mHeight - glyphH >= glyphH) {
+                    // There's enough height left over to create a new CacheBlock
+                    CacheBlock *newBlock = new CacheBlock(oldX, glyphH, roundedUpW,
+                            mHeight - glyphH);
+#if DEBUG_FONT_RENDERER
+                    ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
+                            newBlock, newBlock->mX, newBlock->mY,
+                            newBlock->mWidth, newBlock->mHeight);
+#endif
+                    mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock);
+                }
+            } else {
+                // Insert into current column and adjust column dimensions
+                cacheBlock->mY += glyphH;
+                cacheBlock->mHeight -= glyphH;
+#if DEBUG_FONT_RENDERER
+                ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d",
+                        cacheBlock, cacheBlock->mX, cacheBlock->mY,
+                        cacheBlock->mWidth, cacheBlock->mHeight);
+#endif
+            }
+            if (cacheBlock->mHeight < fmin(glyphH, glyphW)) {
+                // If remaining space in this block is too small to be useful, remove it
+                mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock);
+            }
+            mDirty = true;
+#if DEBUG_FONT_RENDERER
+            ALOGD("fitBitmap: current block list:");
+            mCacheBlocks->output();
+#endif
+            ++mNumGlyphs;
+            return true;
+        }
+        cacheBlock = cacheBlock->mNext;
     }
-
+#if DEBUG_FONT_RENDERER
+    ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH);
+#endif
     return false;
 }
 
@@ -87,10 +213,10 @@
     }
 }
 
-void Font::invalidateTextureCache(CacheTextureLine *cacheLine) {
+void Font::invalidateTextureCache(CacheTexture *cacheTexture) {
     for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
         CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
-        if (cacheLine == NULL || cachedGlyph->mCachedTextureLine == cacheLine) {
+        if (cacheTexture == NULL || cachedGlyph->mCacheTexture == cacheTexture) {
             cachedGlyph->mIsValid = false;
         }
     }
@@ -134,7 +260,7 @@
     mState->appendMeshQuad(nPenX, nPenY, u1, v2,
             nPenX + width, nPenY, u2, v2,
             nPenX + width, nPenY - height, u2, v1,
-            nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture);
+            nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
 }
 
 void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
@@ -145,7 +271,7 @@
     uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
     uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
 
-    CacheTexture *cacheTexture = glyph->mCachedTextureLine->mCacheTexture;
+    CacheTexture *cacheTexture = glyph->mCacheTexture;
     uint32_t cacheWidth = cacheTexture->mWidth;
     const uint8_t* cacheBuffer = cacheTexture->mTexture;
 
@@ -199,7 +325,7 @@
             position->fY + destination[2].fY, u2, v1,
             position->fX + destination[3].fX,
             position->fY + destination[3].fY, u1, v1,
-            glyph->mCachedTextureLine->mCacheTexture);
+            glyph->mCacheTexture);
 }
 
 CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
@@ -297,6 +423,27 @@
     render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
 }
 
+void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
+
+    if (numGlyphs == 0 || text == NULL) {
+        return;
+    }
+    int glyphsCount = 0;
+
+    while (glyphsCount < numGlyphs) {
+        glyph_t glyph = GET_GLYPH(text);
+
+        // Reached the end of the string
+        if (IS_END_OF_STRING(glyph)) {
+            break;
+        }
+
+        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+
+        glyphsCount++;
+    }
+}
+
 void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
         int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
         uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
@@ -409,8 +556,8 @@
     glyph->mBitmapWidth = skiaGlyph.fWidth;
     glyph->mBitmapHeight = skiaGlyph.fHeight;
 
-    uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth;
-    uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight;
+    uint32_t cacheWidth = glyph->mCacheTexture->mWidth;
+    uint32_t cacheHeight = glyph->mCacheTexture->mHeight;
 
     glyph->mBitmapMinU = startX / (float) cacheWidth;
     glyph->mBitmapMinV = startY / (float) cacheHeight;
@@ -473,10 +620,6 @@
     mTextMeshPtr = NULL;
     mCurrentCacheTexture = NULL;
     mLastCacheTexture = NULL;
-    mCacheTextureSmall = NULL;
-    mCacheTexture128 = NULL;
-    mCacheTexture256 = NULL;
-    mCacheTexture512 = NULL;
 
     mLinearFiltering = false;
 
@@ -512,10 +655,10 @@
 }
 
 FontRenderer::~FontRenderer() {
-    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
-        delete mCacheLines[i];
+    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+        delete mCacheTextures[i];
     }
-    mCacheLines.clear();
+    mCacheTextures.clear();
 
     if (mInitialized) {
         // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
@@ -523,10 +666,6 @@
         glDeleteBuffers(1, &mIndexBufferID);
 
         delete[] mTextMeshPtr;
-        delete mCacheTextureSmall;
-        delete mCacheTexture128;
-        delete mCacheTexture256;
-        delete mCacheTexture512;
     }
 
     Vector<Font*> fontsToDereference = mActiveFonts;
@@ -545,9 +684,22 @@
         mActiveFonts[i]->invalidateTextureCache();
     }
 
-    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
-        mCacheLines[i]->mCurrentCol = 0;
+    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+        mCacheTextures[i]->init();
     }
+
+    #if DEBUG_FONT_RENDERER
+    uint16_t totalGlyphs = 0;
+    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+        totalGlyphs += mCacheTextures[i]->mNumGlyphs;
+        // Erase caches, just as a debugging facility
+        if (mCacheTextures[i]->mTexture) {
+            memset(mCacheTextures[i]->mTexture, 0,
+                    mCacheTextures[i]->mWidth * mCacheTextures[i]->mHeight);
+        }
+    }
+    ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
+#endif
 }
 
 void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
@@ -560,29 +712,17 @@
 }
 
 void FontRenderer::flushLargeCaches() {
-    if ((!mCacheTexture128 || !mCacheTexture128->mTexture) &&
-            (!mCacheTexture256 || !mCacheTexture256->mTexture) &&
-            (!mCacheTexture512 || !mCacheTexture512->mTexture)) {
-        // Typical case; no large glyph caches allocated
-        return;
-    }
-
-    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
-        CacheTextureLine* cacheLine = mCacheLines[i];
-        if ((cacheLine->mCacheTexture == mCacheTexture128 ||
-                cacheLine->mCacheTexture == mCacheTexture256 ||
-                cacheLine->mCacheTexture == mCacheTexture512) &&
-                cacheLine->mCacheTexture->mTexture != NULL) {
-            cacheLine->mCurrentCol = 0;
-            for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
-                mActiveFonts[i]->invalidateTextureCache(cacheLine);
+    // Start from 1; don't deallocate smallest/default texture
+    for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
+        CacheTexture* cacheTexture = mCacheTextures[i];
+        if (cacheTexture->mTexture != NULL) {
+            cacheTexture->init();
+            for (uint32_t j = 0; j < mActiveFonts.size(); j++) {
+                mActiveFonts[j]->invalidateTextureCache(cacheTexture);
             }
+            deallocateTextureMemory(cacheTexture);
         }
     }
-
-    deallocateTextureMemory(mCacheTexture128);
-    deallocateTextureMemory(mCacheTexture256);
-    deallocateTextureMemory(mCacheTexture512);
 }
 
 void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
@@ -610,12 +750,25 @@
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 }
 
+CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
+        uint32_t* startX, uint32_t* startY) {
+    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+        if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
+            return mCacheTextures[i];
+        }
+    }
+    // Could not fit glyph into current cache textures
+    return NULL;
+}
+
 void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
         uint32_t* retOriginX, uint32_t* retOriginY) {
+    checkInit();
     cachedGlyph->mIsValid = false;
     // If the glyph is too tall, don't cache it
-    if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
-        ALOGE("Font size to large to fit in cache. width, height = %i, %i",
+    if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
+                mCacheTextures[mCacheTextures.size() - 1]->mHeight) {
+        ALOGE("Font size too large to fit in cache. width, height = %i, %i",
                 (int) glyph.fWidth, (int) glyph.fHeight);
         return;
     }
@@ -624,36 +777,22 @@
     uint32_t startX = 0;
     uint32_t startY = 0;
 
-    bool bitmapFit = false;
-    CacheTextureLine *cacheLine;
-    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
-        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
-        if (bitmapFit) {
-            cacheLine = mCacheLines[i];
-            break;
-        }
-    }
+    CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
 
     // If the new glyph didn't fit, flush the state so far and invalidate everything
-    if (!bitmapFit) {
+    if (!cacheTexture) {
         flushAllAndInvalidate();
 
         // Try to fit it again
-        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
-            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
-            if (bitmapFit) {
-                cacheLine = mCacheLines[i];
-                break;
-            }
-        }
+        cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
 
         // if we still don't fit, something is wrong and we shouldn't draw
-        if (!bitmapFit) {
+        if (!cacheTexture) {
             return;
         }
     }
 
-    cachedGlyph->mCachedTextureLine = cacheLine;
+    cachedGlyph->mCacheTexture = cacheTexture;
 
     *retOriginX = startX;
     *retOriginY = startY;
@@ -661,9 +800,8 @@
     uint32_t endX = startX + glyph.fWidth;
     uint32_t endY = startY + glyph.fHeight;
 
-    uint32_t cacheWidth = cacheLine->mMaxWidth;
+    uint32_t cacheWidth = cacheTexture->mWidth;
 
-    CacheTexture* cacheTexture = cacheLine->mCacheTexture;
     if (!cacheTexture->mTexture) {
         // Large-glyph texture memory is allocated only as needed
         allocateTextureMemory(cacheTexture);
@@ -716,17 +854,10 @@
 }
 
 void FontRenderer::initTextTexture() {
-    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
-        delete mCacheLines[i];
+    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+        delete mCacheTextures[i];
     }
-    mCacheLines.clear();
-
-    if (mCacheTextureSmall) {
-        delete mCacheTextureSmall;
-        delete mCacheTexture128;
-        delete mCacheTexture256;
-        delete mCacheTexture512;
-    }
+    mCacheTextures.clear();
 
     // Next, use other, separate caches for large glyphs.
     uint16_t maxWidth = 0;
@@ -738,35 +869,12 @@
         maxWidth = MAX_TEXT_CACHE_WIDTH;
     }
 
-    mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
-    mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
-    mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
-    mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
-    mCurrentCacheTexture = mCacheTextureSmall;
-
     mUploadTexture = false;
-    // Split up our default cache texture into lines of certain widths
-    int nextLine = 0;
-    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
-    nextLine += mCacheLines.top()->mMaxHeight;
-    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
-    nextLine += mCacheLines.top()->mMaxHeight;
-    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
-    nextLine += mCacheLines.top()->mMaxHeight;
-    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
-    nextLine += mCacheLines.top()->mMaxHeight;
-    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
-    nextLine += mCacheLines.top()->mMaxHeight;
-    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
-    nextLine += mCacheLines.top()->mMaxHeight;
-    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
-            nextLine, 0, mCacheTextureSmall));
-
-    //  The first cache is split into 2 lines of height 128, the rest have just one cache line.
-    mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
-    mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
-    mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
-    mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
+    mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
+    mCacheTextures.push(createCacheTexture(maxWidth, 256, false));
+    mCacheTextures.push(createCacheTexture(maxWidth, 256, false));
+    mCacheTextures.push(createCacheTexture(maxWidth, 512, false));
+    mCurrentCacheTexture = mCacheTextures[0];
 }
 
 // Avoid having to reallocate memory and render quad by quad
@@ -821,26 +929,28 @@
 
     Caches& caches = Caches::getInstance();
     GLuint lastTextureId = 0;
-    // Iterate over all the cache lines and see which ones need to be updated
-    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
-        CacheTextureLine* cl = mCacheLines[i];
-        if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
-            CacheTexture* cacheTexture = cl->mCacheTexture;
+    // Iterate over all the cache textures and see which ones need to be updated
+    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+        CacheTexture* cacheTexture = mCacheTextures[i];
+        if (cacheTexture->mDirty && cacheTexture->mTexture != NULL) {
             uint32_t xOffset = 0;
-            uint32_t yOffset = cl->mCurrentRow;
-            uint32_t width   = cl->mMaxWidth;
-            uint32_t height  = cl->mMaxHeight;
-            void* textureData = cacheTexture->mTexture + (yOffset * width);
+            uint32_t width   = cacheTexture->mWidth;
+            uint32_t height  = cacheTexture->mHeight;
+            void* textureData = cacheTexture->mTexture;
 
             if (cacheTexture->mTextureId != lastTextureId) {
                 caches.activeTexture(0);
                 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
                 lastTextureId = cacheTexture->mTextureId;
             }
-            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
+#if DEBUG_FONT_RENDERER
+            ALOGD("glTextSubimage for cacheTexture %d: xOff, width height = %d, %d, %d",
+                    i, xOffset, width, height);
+#endif
+            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, 0, width, height,
                     GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
 
-            cl->mDirty = false;
+            cacheTexture->mDirty = false;
         }
     }
 
@@ -960,43 +1070,7 @@
     }
 }
 
-uint32_t FontRenderer::getRemainingCacheCapacity() {
-    uint32_t remainingCapacity = 0;
-    float totalPixels = 0;
-
-    //avoid divide by zero if the size is 0
-    if (mCacheLines.size() == 0) {
-        return 0;
-    }
-    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
-         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
-         totalPixels += mCacheLines[i]->mMaxWidth;
-    }
-    remainingCapacity = (remainingCapacity * 100) / totalPixels;
-    return remainingCapacity;
-}
-
-void FontRenderer::precacheLatin(SkPaint* paint) {
-    // Remaining capacity is measured in %
-    uint32_t remainingCapacity = getRemainingCacheCapacity();
-    uint32_t precacheIndex = 0;
-
-    // We store a string with letters in a rough frequency of occurrence
-    String16 l("eisarntolcdugpmhbyfvkwzxjq EISARNTOLCDUGPMHBYFVKWZXJQ,.?!()-+@;:'0123456789");
-
-    size_t size = l.size();
-    uint16_t latin[size];
-    paint->utfToGlyphs(l.string(), SkPaint::kUTF16_TextEncoding, size * sizeof(char16_t), latin);
-
-    while (remainingCapacity > 25 && precacheIndex < size) {
-        mCurrentFont->getCachedGlyph(paint, TO_GLYPH(latin[precacheIndex]));
-        remainingCapacity = getRemainingCacheCapacity();
-        precacheIndex++;
-    }
-}
-
 void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
-    uint32_t currentNumFonts = mActiveFonts.size();
     int flags = 0;
     if (paint->isFakeBoldText()) {
         flags |= Font::kFakeBold;
@@ -1012,12 +1086,6 @@
     mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
             scaleX, style, strokeWidth);
 
-    const float maxPrecacheFontSize = 40.0f;
-    bool isNewFont = currentNumFonts != mActiveFonts.size();
-
-    if (isNewFont && fontSize <= maxPrecacheFontSize) {
-        precacheLatin(paint);
-    }
 }
 
 FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
@@ -1084,6 +1152,25 @@
     }
 }
 
+void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
+    int flags = 0;
+    if (paint->isFakeBoldText()) {
+        flags |= Font::kFakeBold;
+    }
+    const float skewX = paint->getTextSkewX();
+    uint32_t italicStyle = *(uint32_t*) &skewX;
+    const float scaleXFloat = paint->getTextScaleX();
+    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
+    SkPaint::Style style = paint->getStyle();
+    const float strokeWidthFloat = paint->getStrokeWidth();
+    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
+    float fontSize = paint->getTextSize();
+    Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
+            fontSize, flags, italicStyle, scaleX, style, strokeWidth);
+
+    font->precache(paint, text, numGlyphs);
+}
+
 bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
         uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
     if (!mCurrentFont) {
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 9ed6932..febae17 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -53,17 +53,63 @@
     #define IS_END_OF_STRING(glyph) glyph < 0
 #endif
 
+#define TEXTURE_BORDER_SIZE 1
+
 ///////////////////////////////////////////////////////////////////////////////
 // Declarations
 ///////////////////////////////////////////////////////////////////////////////
 
 class FontRenderer;
 
+/**
+ * CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
+ * Using CacheBlocks enables us to pack the cache from top to bottom as well as left to right.
+ * When we add a glyph to the cache, we see if it fits within one of the existing columns that
+ * have already been started (this is the case if the glyph fits vertically as well as
+ * horizontally, and if its width is sufficiently close to the column width to avoid
+ * sub-optimal packing of small glyphs into wide columns). If there is no column in which the
+ * glyph fits, we check the final node, which is the remaining space in the cache, creating
+ * a new column as appropriate.
+ *
+ * As columns fill up, we remove their CacheBlock from the list to avoid having to check
+ * small blocks in the future.
+ */
+struct CacheBlock {
+    uint16_t mX;
+    uint16_t mY;
+    uint16_t mWidth;
+    uint16_t mHeight;
+    CacheBlock* mNext;
+    CacheBlock* mPrev;
+
+    CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height, bool empty = false):
+        mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL)
+    {
+    }
+
+    static CacheBlock* insertBlock(CacheBlock* head, CacheBlock *newBlock);
+
+    static CacheBlock* removeBlock(CacheBlock* head, CacheBlock *blockToRemove);
+
+    void output() {
+        CacheBlock *currBlock = this;
+        while (currBlock) {
+            ALOGD("Block: this, x, y, w, h = %p, %d, %d, %d, %d",
+                    currBlock, currBlock->mX, currBlock->mY, currBlock->mWidth, currBlock->mHeight);
+            currBlock = currBlock->mNext;
+        }
+    }
+};
+
 class CacheTexture {
 public:
     CacheTexture(uint16_t width, uint16_t height) :
             mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
-            mLinearFiltering(false) { }
+            mLinearFiltering(false), mDirty(false), mNumGlyphs(0) {
+        mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
+                mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
+    }
+
     ~CacheTexture() {
         if (mTexture) {
             delete[] mTexture;
@@ -71,35 +117,36 @@
         if (mTextureId) {
             glDeleteTextures(1, &mTextureId);
         }
+        reset();
     }
 
+    void reset() {
+        // Delete existing cache blocks
+        while (mCacheBlocks != NULL) {
+            CacheBlock* tmpBlock = mCacheBlocks;
+            mCacheBlocks = mCacheBlocks->mNext;
+            delete tmpBlock;
+        }
+        mNumGlyphs = 0;
+    }
+
+    void init() {
+        // reset, then create a new remainder space to start again
+        reset();
+        mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
+                mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
+    }
+
+    bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
+
     uint8_t* mTexture;
     GLuint mTextureId;
     uint16_t mWidth;
     uint16_t mHeight;
     bool mLinearFiltering;
-};
-
-class CacheTextureLine {
-public:
-    CacheTextureLine(uint16_t maxWidth, uint16_t maxHeight, uint32_t currentRow,
-            uint32_t currentCol, CacheTexture* cacheTexture):
-                mMaxHeight(maxHeight),
-                mMaxWidth(maxWidth),
-                mCurrentRow(currentRow),
-                mCurrentCol(currentCol),
-                mDirty(false),
-                mCacheTexture(cacheTexture) {
-    }
-
-    bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
-
-    uint16_t mMaxHeight;
-    uint16_t mMaxWidth;
-    uint32_t mCurrentRow;
-    uint32_t mCurrentCol;
     bool mDirty;
-    CacheTexture* mCacheTexture;
+    uint16_t mNumGlyphs;
+    CacheBlock* mCacheBlocks;
 };
 
 struct CachedGlyphInfo {
@@ -127,7 +174,7 @@
     // Auto-kerning
     SkFixed mLsbDelta;
     SkFixed mRsbDelta;
-    CacheTextureLine* mCachedTextureLine;
+    CacheTexture* mCacheTexture;
 };
 
 
@@ -179,6 +226,8 @@
         MEASURE,
     };
 
+    void precache(SkPaint* paint, const char* text, int numGlyphs);
+
     void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
             int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
             uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
@@ -192,7 +241,7 @@
     // Cache of glyphs
     DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
 
-    void invalidateTextureCache(CacheTextureLine *cacheLine = NULL);
+    void invalidateTextureCache(CacheTexture *cacheTexture = NULL);
 
     CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph);
     void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph);
@@ -244,6 +293,9 @@
     }
 
     void setFont(SkPaint* paint, uint32_t fontId, float fontSize);
+
+    void precache(SkPaint* paint, const char* text, int numGlyphs);
+
     // bounds is an out parameter
     bool renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
             uint32_t len, int numGlyphs, int x, int y, Rect* bounds);
@@ -293,17 +345,11 @@
 
     uint32_t getCacheSize() const {
         uint32_t size = 0;
-        if (mCacheTextureSmall != NULL && mCacheTextureSmall->mTexture != NULL) {
-            size += mCacheTextureSmall->mWidth * mCacheTextureSmall->mHeight;
-        }
-        if (mCacheTexture128 != NULL && mCacheTexture128->mTexture != NULL) {
-            size += mCacheTexture128->mWidth * mCacheTexture128->mHeight;
-        }
-        if (mCacheTexture256 != NULL && mCacheTexture256->mTexture != NULL) {
-            size += mCacheTexture256->mWidth * mCacheTexture256->mHeight;
-        }
-        if (mCacheTexture512 != NULL && mCacheTexture512->mTexture != NULL) {
-            size += mCacheTexture512->mWidth * mCacheTexture512->mHeight;
+        for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+            CacheTexture* cacheTexture = mCacheTextures[i];
+            if (cacheTexture != NULL && cacheTexture->mTexture != NULL) {
+                size += cacheTexture->mWidth * cacheTexture->mHeight;
+            }
         }
         return size;
     }
@@ -319,6 +365,7 @@
     CacheTexture* createCacheTexture(int width, int height, bool allocate);
     void cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
             uint32_t *retOriginX, uint32_t *retOriginY);
+    CacheTexture* cacheBitmapInTexture(const SkGlyph& glyph, uint32_t* startX, uint32_t* startY);
 
     void flushAllAndInvalidate();
     void initVertexArrayBuffers();
@@ -327,8 +374,6 @@
     void initRender(const Rect* clip, Rect* bounds);
     void finishRender();
 
-    void precacheLatin(SkPaint* paint);
-
     void issueDrawCommand();
     void appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
             float x2, float y2, float u2, float v2,
@@ -346,18 +391,13 @@
     uint32_t mSmallCacheWidth;
     uint32_t mSmallCacheHeight;
 
-    Vector<CacheTextureLine*> mCacheLines;
-    uint32_t getRemainingCacheCapacity();
+    Vector<CacheTexture*> mCacheTextures;
 
     Font* mCurrentFont;
     Vector<Font*> mActiveFonts;
 
     CacheTexture* mCurrentCacheTexture;
     CacheTexture* mLastCacheTexture;
-    CacheTexture* mCacheTextureSmall;
-    CacheTexture* mCacheTexture128;
-    CacheTexture* mCacheTexture256;
-    CacheTexture* mCacheTexture512;
 
     void checkTextureUpdate();
     bool mUploadTexture;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 6639231..849c556 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -250,6 +250,7 @@
     glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight());
     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
 
+    mCaches.scissorEnabled = glIsEnabled(GL_SCISSOR_TEST);
     mCaches.enableScissor();
     mCaches.resetScissor();
     dirtyClip();
diff --git a/location/java/android/location/Criteria.java b/location/java/android/location/Criteria.java
index 6258a43..68fb4a0 100644
--- a/location/java/android/location/Criteria.java
+++ b/location/java/android/location/Criteria.java
@@ -24,7 +24,9 @@
  * location provider.  Providers maybe ordered according to accuracy,
  * power usage, ability to report altitude, speed,
  * and bearing, and monetary cost.
- * @deprecated {@link LocationRequest} instead
+ *
+ * @deprecated use {@link LocationRequest} instead, and also see notes
+ * at {@link LocationManager}
  */
 @Deprecated
 public class Criteria implements Parcelable {
diff --git a/location/java/android/location/Geofence.java b/location/java/android/location/Geofence.java
index 353a1ca..03cca84 100644
--- a/location/java/android/location/Geofence.java
+++ b/location/java/android/location/Geofence.java
@@ -20,7 +20,11 @@
 import android.os.Parcelable;
 
 /**
- * Represents a Geofence
+ * Represents a geographical boundary, also known as a geofence.
+ *
+ * <p>Currently only circular geofences are supported, but this object
+ * is opaque so could be used in the future to represent polygons or other
+ * shapes.
  */
 public final class Geofence implements Parcelable {
     /** @hide */
@@ -33,6 +37,7 @@
 
     /**
      * Create a horizontal, circular geofence.
+     *
      * @param latitude latitude in degrees
      * @param longitude longitude in degrees
      * @param radius radius in meters
@@ -152,6 +157,9 @@
         return result;
     }
 
+    /**
+     * Two geofences are equal if they have identical properties.
+     */
     @Override
     public boolean equals(Object obj) {
         if (this == obj)
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index ece4500..f50ee82 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -27,15 +27,15 @@
 import java.util.StringTokenizer;
 
 /**
- * A class representing a geographic location sensed at a particular
- * time (a "fix").  A location consists of a latitude and longitude, a
- * UTC timestamp. and optionally information on altitude, speed, and
- * bearing.
+ * A data class representing a geographic location.
  *
- * <p> Information specific to a particular provider or class of
- * providers may be communicated to the application using getExtras,
- * which returns a Bundle of key/value pairs.  Each provider will only
- * provide those entries for which information is available.
+ * <p>A location can consist of a latitude, longitude, timestamp,
+ * and other information such as bearing, altitude and velocity.
+ *
+ * <p>All locations generated by the {@link LocationManager} are
+ * guaranteed to have a valid latitude, longitude, and timestamp
+ * (both UTC time and elapsed real-time since boot), all other
+ * parameters are optional.
  */
 public class Location implements Parcelable {
     /**
@@ -86,20 +86,19 @@
     private float[] mResults = new float[2];
 
     /**
-     * Constructs a new Location.  By default, time, latitude,
-     * longitude, and numSatellites are 0; hasAltitude, hasSpeed, and
-     * hasBearing are false; and there is no extra information.
+     * Construct a new Location with a named provider.
      *
-     * @param provider the name of the location provider that generated this
-     * location fix.
+     * <p>By default time, latitude and longitude are 0, and the location
+     * has no bearing, altitude, speed, accuracy or extras.
+     *
+     * @param provider the name of the provider that generated this location
      */
     public Location(String provider) {
         mProvider = provider;
     }
 
     /**
-     * Constructs a new Location object that is a copy of the given
-     * location.
+     * Construct a new Location object that is copied from an existing one.
      */
     public Location(Location l) {
         set(l);
@@ -447,9 +446,19 @@
     }
 
     /**
-     * Returns the name of the provider that generated this fix,
-     * or null if it is not associated with a provider.
+     * Returns the name of the provider that generated this fix.
+     *
+     * <p class="note">At API version 17 we deprecated {@link LocationProvider}
+     * and all API methods that request a provider by name. The new API methods
+     * will produce locations that could come from different sources, and even
+     * locations that are fused from several sources. So you should generally
+     * not care what provider is associated with a location object.
+     *
+     * @return the provider, or null if it has not been set
+     *
+     * @deprecated locations can now be sourced from many providers, or even fused
      */
+    @Deprecated
     public String getProvider() {
         return mProvider;
     }
@@ -462,16 +471,19 @@
     }
 
     /**
-     * Return the UTC time of this fix, in milliseconds since January 1,
-     * 1970.
+     * Return the UTC time of this fix, in milliseconds since January 1, 1970.
+     *
      * <p>Note that the UTC time on a device is not monotonic: it
      * can jump forwards or backwards unpredictably. So always use
-     * {@link #getElapsedRealtimeNano()} when calculating time deltas.
-     * <p>On the other hand, {@link #getTime()} is useful for presenting
+     * {@link #getElapsedRealtimeNano} when calculating time deltas.
+     *
+     * <p>On the other hand, {@link #getTime} is useful for presenting
      * a human readable time to the user, or for carefully comparing
      * location fixes across reboot or across devices.
-     * <p>This method will always return a valid timestamp on
-     * Locations generated by a {@link LocationProvider}.
+     *
+     * <p>All locations generated by the {@link LocationManager}
+     * are guaranteed to have a valid UTC time, however remember that
+     * the system time may have changed since the location was generated.
      *
      * @return time of fix, in milliseconds since January 1, 1970.
      */
@@ -491,13 +503,16 @@
 
     /**
      * Return the time of this fix, in elapsed real-time since system boot.
+     *
      * <p>This value can be reliably compared to
-     * {@link android.os.SystemClock#elapsedRealtimeNano()},
-     * to calculate the age of a fix, and to compare Location fixes, since
-     * elapsed real-time is guaranteed monotonic for each system boot, and
-     * continues to increment even when the system is in deep sleep.
-     * <p>This method will always return a valid timestamp on
-     * Locations generated by a {@link LocationProvider}.
+     * {@link android.os.SystemClock#elapsedRealtimeNano},
+     * to calculate the age of a fix and to compare Location fixes. This
+     * is reliable because elapsed real-time is guaranteed monotonic for
+     * each system boot and continues to increment even when the system
+     * is in deep sleep (unlike {@link #getTime}.
+     *
+     * <p>All locations generated by the {@link LocationManager}
+     * are guaranteed to have a valid elapsed real-time.
      *
      * @return elapsed real-time of fix, in nanoseconds since system boot.
      */
@@ -515,56 +530,59 @@
     }
 
     /**
-     * Return the latitude of this fix.
-     * <p>This method will always return a valid latitude on
-     * Locations generated by a {@link LocationProvider}.
+     * Get the latitude, in degrees.
+     *
+     * <p>All locations generated by the {@link LocationManager}
+     * will have a valid latitude.
      */
     public double getLatitude() {
         return mLatitude;
     }
 
     /**
-     * Sets the latitude of this fix.
+     * Set the latitude, in degrees.
      */
     public void setLatitude(double latitude) {
         mLatitude = latitude;
     }
 
     /**
-     * Return the longitude of this fix.
-     * <p>This method will always return a valid longitude on
-     * Locations generated by a {@link LocationProvider}.
+     * Get the longitude, in degrees.
+     *
+     * <p>All locations generated by the {@link LocationManager}
+     * will have a valid longitude.
      */
     public double getLongitude() {
         return mLongitude;
     }
 
     /**
-     * Sets the longitude of this fix.
+     * Set the longitude, in degrees.
      */
     public void setLongitude(double longitude) {
         mLongitude = longitude;
     }
 
     /**
-     * Returns true if this fix contains altitude information, false
-     * otherwise.
+     * True if this location has an altitude.
      */
     public boolean hasAltitude() {
         return mHasAltitude;
     }
 
     /**
-     * Returns the altitude of this fix.  If {@link #hasAltitude} is false,
-     * 0.0f is returned.
+     * Get the altitude if available, in meters above sea level.
+     *
+     * <p>If this location does not have an altitude then 0.0 is returned.
      */
     public double getAltitude() {
         return mAltitude;
     }
 
     /**
-     * Sets the altitude of this fix.  Following this call,
-     * hasAltitude() will return true.
+     * Set the altitude, in meters above sea level.
+     *
+     * <p>Following this call {@link #hasAltitude} will return true.
      */
     public void setAltitude(double altitude) {
         mAltitude = altitude;
@@ -572,8 +590,10 @@
     }
 
     /**
-     * Clears the altitude of this fix.  Following this call,
-     * hasAltitude() will return false.
+     * Remove the altitude from this location.
+     *
+     * <p>Following this call {@link #hasAltitude} will return false,
+     * and {@link #getAltitude} will return 0.0.
      */
     public void removeAltitude() {
         mAltitude = 0.0f;
@@ -581,24 +601,25 @@
     }
 
     /**
-     * Returns true if this fix contains speed information, false
-     * otherwise.  The default implementation returns false.
+     * True if this location has a speed.
      */
     public boolean hasSpeed() {
         return mHasSpeed;
     }
 
     /**
-     * Returns the speed of the device over ground in meters/second.
-     * If hasSpeed() is false, 0.0f is returned.
+     * Get the speed if it is available, in meters/second over ground.
+     *
+     * <p>If this location does not have a speed then 0.0 is returned.
      */
     public float getSpeed() {
         return mSpeed;
     }
 
     /**
-     * Sets the speed of this fix, in meters/second.  Following this
-     * call, hasSpeed() will return true.
+     * Set the speed, in meters/second over ground.
+     *
+     * <p>Following this call {@link #hasSpeed} will return true.
      */
     public void setSpeed(float speed) {
         mSpeed = speed;
@@ -606,8 +627,10 @@
     }
 
     /**
-     * Clears the speed of this fix.  Following this call, hasSpeed()
-     * will return false.
+     * Remove the speed from this location.
+     *
+     * <p>Following this call {@link #hasSpeed} will return false,
+     * and {@link #getSpeed} will return 0.0.
      */
     public void removeSpeed() {
         mSpeed = 0.0f;
@@ -615,24 +638,32 @@
     }
 
     /**
-     * Returns true if the provider is able to report bearing information,
-     * false otherwise.  The default implementation returns false.
+     * True if this location has a bearing.
      */
     public boolean hasBearing() {
         return mHasBearing;
     }
 
     /**
-     * Returns the direction of travel in degrees East of true
-     * North. If hasBearing() is false, 0.0 is returned.
+     * Get the bearing, in degrees.
+     *
+     * <p>Bearing is the horizontal direction of travel of this device,
+     * and is not related to the device orientation. It is guaranteed to
+     * be in the range (0.0, 360.0] if the device has a bearing.
+     *
+     * <p>If this location does not have a bearing then 0.0 is returned.
      */
     public float getBearing() {
         return mBearing;
     }
 
     /**
-     * Sets the bearing of this fix.  Following this call, hasBearing()
-     * will return true.
+     * Set the bearing, in degrees.
+     *
+     * <p>Bearing is the horizontal direction of travel of this device,
+     * and is not related to the device orientation.
+     *
+     * <p>The input will be wrapped into the range (0.0, 360.0].
      */
     public void setBearing(float bearing) {
         while (bearing < 0.0f) {
@@ -646,8 +677,10 @@
     }
 
     /**
-     * Clears the bearing of this fix.  Following this call, hasBearing()
-     * will return false.
+     * Remove the bearing from this location.
+     *
+     * <p>Following this call {@link #hasBearing} will return false,
+     * and {@link #getBearing} will return 0.0.
      */
     public void removeBearing() {
         mBearing = 0.0f;
@@ -655,35 +688,47 @@
     }
 
     /**
-     * Return true if this Location has an associated accuracy.
-     * <p>All Location objects generated by a {@link LocationProvider}
-     * will have an accuracy.
+     * True if this location has an accuracy.
+     *
+     * <p>All locations generated by the {@link LocationManager} have an
+     * accuracy.
      */
     public boolean hasAccuracy() {
         return mHasAccuracy;
     }
 
     /**
-     * Return the accuracy of this Location fix.
-     * <p>Accuracy is measured in meters, and indicates the
-     * radius of 95% confidence.
-     * In other words, there is a 95% probability that the
-     * true location is within a circle centered at the reported
-     * location, with radius of the reported accuracy.
-     * <p>This is only a measure of horizontal accuracy, and does
-     * not indicate the accuracy of bearing, velocity or altitude
-     * if those are included in this Location.
-     * <p>If {@link #hasAccuracy} is false, 0.0 is returned.
-     * <p>All Location object generated by a {@link LocationProvider}
-     * will have a valid accuracy.
+     * Get the estimated accuracy of this location, in meters.
+     *
+     * <p>We define accuracy as the radius of 68% confidence. In other
+     * words, if you draw a circle centered at this location's
+     * latitude and longitude, and with a radius equal to the accuracy,
+     * then there is a 68% probability that the true location is inside
+     * the circle.
+     *
+     * <p>In statistical terms, it is assumed that location errors
+     * are random with a normal distribution, so the 68% confidence circle
+     * represents one standard deviation. Note that in practice, location
+     * errors do not always follow such a simple distribution.
+     *
+     * <p>This accuracy estimation is only concerned with horizontal
+     * accuracy, and does not indicate the accuracy of bearing,
+     * velocity or altitude if those are included in this Location.
+     *
+     * <p>If this location does not have an accuracy, then 0.0 is returned.
+     * All locations generated by the {@link LocationManager} include
+     * an accuracy.
      */
     public float getAccuracy() {
         return mAccuracy;
     }
 
     /**
-     * Sets the accuracy of this fix.  Following this call, hasAccuracy()
-     * will return true.
+     * Set the estimated accuracy of this location, meters.
+     *
+     * <p>See {@link #getAccuracy} for the definition of accuracy.
+     *
+     * <p>Following this call {@link #hasAccuracy} will return true.
      */
     public void setAccuracy(float accuracy) {
         mAccuracy = accuracy;
@@ -691,8 +736,10 @@
     }
 
     /**
-     * Clears the accuracy of this fix.  Following this call, hasAccuracy()
-     * will return false.
+     * Remove the accuracy from this location.
+     *
+     * <p>Following this call {@link #hasAccuracy} will return false, and
+     * {@link #getAccuracy} will return 0.0.
      */
     public void removeAccuracy() {
         mAccuracy = 0.0f;
@@ -700,8 +747,14 @@
     }
 
     /**
-     * Return true if this Location object has enough data set to
-     * be considered a valid fix from a {@link LocationProvider}.
+     * Return true if this Location object is complete.
+     *
+     * <p>A location object is currently considered complete if it has
+     * a valid provider, accuracy, wall-clock time and elapsed real-time.
+     *
+     * <p>All locations supplied by the {@link LocationManager} to
+     * applications must be complete.
+     *
      * @see #makeComplete
      * @hide
      */
@@ -714,9 +767,11 @@
     }
 
     /**
-     * Helper to fill in incomplete fields.
-     * Only use this to assist in backwards compatibility
-     * with Location objects received from applications.
+     * Helper to fill incomplete fields.
+     *
+     * <p>Used to assist in backwards compatibility with
+     * Location objects received from applications.
+     *
      * @see #isComplete
      * @hide
      */
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 5f87c84..25da208 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -46,12 +46,41 @@
  * {@link android.content.Context#getSystemService
  * Context.getSystemService(Context.LOCATION_SERVICE)}.
  *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about using location services, read the
- * <a href="{@docRoot}guide/topics/location/index.html">Location and Maps</a>
- * developer guide.</p>
- * </div>
+ * <p>At API version 17 the Location API's were simplified.
+ * Previously applications would need to explicitly enumerate, select, and
+ * track Location Providers (such as GPS or Network).
+ * This has been replaced by the concept of
+ * <em>Fused Location</em>. Now applications just specify the quality of service
+ * required for location updates (using the new {@link LocationRequest} class),
+ * and the system will fuse results from individual location providers
+ * as necessary before returning the result to the application.
+ *
+ * <p>As a result of this change, the {@link LocationProvider} and
+ * {@link Criteria} classes have been deprecated, in favor of
+ * {@link LocationRequest}. Furthermore, all Location Manager
+ * methods involving Criteria or explicitly named Providers have
+ * been deprecated, in favor of new variants that use
+ * {@link LocationRequest}.
+ *
+ * <p>A single {@link LocationRequest} object can trigger the use
+ * of all providers (including GPS, Network, and the passive) provider
+ * as necessary. This should result in a lot less work for your application. You
+ * no longer need to track the status and availability of each
+ * location provider. Just set the quality of locations required
+ * in {@link LocationRequest}, and let the system manage the rest.
+ *
+ * <p class="note">Unless noted, all Location API methods require
+ * the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} or
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permissions.
+ * If your application only has the Coarse permission then it will still
+ * receive location results, but the update rate will be throttled and
+ * the exact location will be obfuscated to a coarse level of accuracy.
+ *
+ * <p> class="note">Before API level 17, the use of 'fine' location
+ * providers such as GPS required the fine permission. As of API level
+ * 17, applications with only the coarse permission may use all providers,
+ * including GPS, but the locations are obfuscated (made coarse) before
+ * being sent to the application.
  */
 public class LocationManager {
     private static final String TAG = "LocationManager";
@@ -65,56 +94,69 @@
     private final GpsStatus mGpsStatus = new GpsStatus();
 
     /**
-     * Name of the network location provider.  This provider determines location based on
+     * Name of the network location provider.
+     * <p>This provider determines location based on
      * availability of cell tower and WiFi access points. Results are retrieved
      * by means of a network lookup.
      *
-     * Requires either of the permissions android.permission.ACCESS_COARSE_LOCATION
-     * or android.permission.ACCESS_FINE_LOCATION.
-     * @deprecated use the {@link Criteria} class instead
+     * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
      */
     @Deprecated
     public static final String NETWORK_PROVIDER = "network";
 
     /**
-     * Name of the GPS location provider. This provider determines location using
+     * Name of the GPS location provider.
+     *
+     * <p>This provider determines location using
      * satellites. Depending on conditions, this provider may take a while to return
      * a location fix.
      *
-     * Requires the permission android.permission.ACCESS_FINE_LOCATION.
+     * <p>Before API version 17, this provider required the
+     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
+     * From API version 17 and onwards, this provider can also be used with
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}, however
+     * the locations returned will be obfuscated to a coarse level of accuracy.
      *
      * <p> The extras Bundle for the GPS location provider can contain the
      * following key/value pairs:
-     *
      * <ul>
      * <li> satellites - the number of satellites used to derive the fix
      * </ul>
-     * @deprecated use the {@link Criteria} class instead
+     *
+     * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
      */
     @Deprecated
     public static final String GPS_PROVIDER = "gps";
 
     /**
      * A special location provider for receiving locations without actually initiating
-     * a location fix. This provider can be used to passively receive location updates
+     * a location fix.
+     *
+     * <p>This provider can be used to passively receive location updates
      * when other applications or services request them without actually requesting
      * the locations yourself.  This provider will return locations generated by other
      * providers.  You can query the {@link Location#getProvider()} method to determine
      * the origin of the location update.
      *
-     * Requires the permission android.permission.ACCESS_FINE_LOCATION, although if the GPS
-     * is not enabled this provider might only return coarse fixes.
-     * @deprecated use the {@link Criteria} class instead
+     * <p>Before API version 17, this provider required the
+     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
+     * From API version 17 and onwards, this provider can also be used with
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}, however
+     * the locations returned will be obfuscated to a coarse level of accuracy.
+     *
+     * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
      */
     @Deprecated
     public static final String PASSIVE_PROVIDER = "passive";
 
     /**
-     * Name of the Fused location provider.<p>
-     * This provider combines inputs for all possible location sources
-     * to provide the best possible Location fix.<p>
+     * Name of the Fused location provider.
      *
-     * Requires the permission android.permission.ACCESS_FINE_LOCATION.
+     * <p>This provider combines inputs for all possible location sources
+     * to provide the best possible Location fix. It is implicitly
+     * used for all API's that involve the {@link LocationRequest}
+     * object.
+     *
      * @hide
      */
     public static final String FUSED_PROVIDER = "fused";
@@ -128,7 +170,8 @@
     /**
      * Key used for a Bundle extra holding an Integer status value
      * when a status change is broadcast using a PendingIntent.
-     * @deprecated use the {@link Criteria} class instead
+     *
+     * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
      */
     @Deprecated
     public static final String KEY_STATUS_CHANGED = "status";
@@ -136,7 +179,8 @@
     /**
      * Key used for a Bundle extra holding an Boolean status value
      * when a provider enabled/disabled event is broadcast using a PendingIntent.
-     * @deprecated use the {@link Criteria} class instead
+     *
+     * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
      */
     @Deprecated
     public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
@@ -153,7 +197,7 @@
      * where {@code true} means enabled.
      * @see #EXTRA_GPS_ENABLED
      *
-     * {@hide}
+     * @hide
      */
     public static final String GPS_ENABLED_CHANGE_ACTION =
         "android.location.GPS_ENABLED_CHANGE";
@@ -161,7 +205,8 @@
     /**
      * Broadcast intent action when the configured location providers
      * change.
-     * @deprecated use the {@link Criteria} class instead
+     *
+     * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
      */
     @Deprecated
     public static final String PROVIDERS_CHANGED_ACTION =
@@ -173,7 +218,7 @@
      * boolean, where {@code true} means that the GPS is actively receiving fixes.
      * @see #EXTRA_GPS_ENABLED
      *
-     * {@hide}
+     * @hide
      */
     public static final String GPS_FIX_CHANGE_ACTION =
         "android.location.GPS_FIX_CHANGE";
@@ -183,7 +228,7 @@
      * disabled. {@code true} means GPS is enabled. Retrieve it with
      * {@link android.content.Intent#getBooleanExtra(String,boolean)}.
      *
-     * {@hide}
+     * @hide
      */
     public static final String EXTRA_GPS_ENABLED = "enabled";
 
@@ -285,6 +330,7 @@
             }
         }
     }
+
     /**
      * @hide - hide this constructor because it has a parameter
      * of type ILocationManager, which is a system private class. The
@@ -301,12 +347,13 @@
     }
 
     /**
-     * Returns a list of the names of all known location providers.  All
-     * providers are returned, including ones that are not permitted to be
-     * accessed by the calling activity or are currently disabled.
+     * Returns a list of the names of all known location providers.
+     * <p>All providers are returned, including ones that are not permitted to
+     * be accessed by the calling activity or are currently disabled.
      *
-     * @return list of Strings containing names of the providers
-     * @deprecated use the {@link Criteria} class instead
+     * @return list of Strings containing names of the provider
+     *
+     * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
      */
     @Deprecated
     public List<String> getAllProviders() {
@@ -319,15 +366,13 @@
     }
 
     /**
-     * Returns a list of the names of location providers.  Only providers that
-     * are permitted to be accessed by the calling activity will be returned.
+     * Returns a list of the names of location providers.
      *
      * @param enabledOnly if true then only the providers which are currently
      * enabled are returned.
      * @return list of Strings containing names of the providers
-     * @deprecated The {@link LocationProvider} class is deprecated. So
-     * use the {@link Criteria} class to request location instead of
-     * enumerating providers.
+     *
+     * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
      */
     @Deprecated
     public List<String> getProviders(boolean enabledOnly) {
@@ -346,12 +391,11 @@
      * @param name the provider name
      * @return a LocationProvider, or null
      *
-     * @throws IllegalArgumentException if name is null or does not exisit
+     * @throws IllegalArgumentException if name is null or does not exist
      * @throws SecurityException if the caller is not permitted to access the
      * given provider.
-     * @deprecated The {@link LocationProvider} class is deprecated. So
-     * use the {@link Criteria} class to request location instead of
-     * enumerating providers.
+     *
+     * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
      */
     @Deprecated
     public LocationProvider getProvider(String name) {
@@ -377,9 +421,8 @@
      * @param enabledOnly if true then only the providers which are currently
      * enabled are returned.
      * @return list of Strings containing names of the providers
-     * @deprecated The {@link LocationProvider} class is deprecated. So
-     * use the {@link Criteria} class to request location instead of
-     * enumerating providers.
+     *
+     * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
      */
     @Deprecated
     public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
@@ -413,7 +456,8 @@
      * @param criteria the criteria that need to be matched
      * @param enabledOnly if true then only a provider that is currently enabled is returned
      * @return name of the provider that best matches the requirements
-     * @deprecated using an explicit provider doesn't allow fused location
+     *
+     * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
      */
     @Deprecated
     public String getBestProvider(Criteria criteria, boolean enabledOnly) {
@@ -427,64 +471,11 @@
     }
 
     /**
-     * Registers the current activity to be notified periodically by
-     * the named provider.  Periodically, the supplied LocationListener will
-     * be called with the current Location or with status updates.
+     * Register for location updates using the named provider, and a
+     * pending intent.
      *
-     * <p> It may take a while to receive the first location update. If
-     * an immediate location is required, applications may use the
-     * {@link #getLastKnownLocation(String)} method.
-     *
-     * <p> In case the provider is disabled by the user, updates will stop,
-     * and the {@link LocationListener#onProviderDisabled(String)}
-     * method will be called. As soon as the provider is enabled again,
-     * the {@link LocationListener#onProviderEnabled(String)} method will
-     * be called and location updates will start again.
-     *
-     * <p> The update interval can be controlled using the minTime parameter.
-     * The elapsed time between location updates will never be less than
-     * minTime, although it can be more depending on the Location Provider
-     * implementation and the update interval requested by other applications.
-     *
-     * <p> Choosing a sensible value for minTime is important to conserve
-     * battery life. Each location update requires power from
-     * GPS, WIFI, Cell and other radios. Select a minTime value as high as
-     * possible while still providing a reasonable user experience.
-     * If your application is not in the foreground and showing
-     * location to the user then your application should avoid using an active
-     * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
-     * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
-     * or greater. If your application is in the foreground and showing
-     * location to the user then it is appropriate to select a faster
-     * update interval.
-     *
-     * <p> The minDistance parameter can also be used to control the
-     * frequency of location updates. If it is greater than 0 then the
-     * location provider will only send your application an update when
-     * the location has changed by at least minDistance meters, AND
-     * at least minTime milliseconds have passed. However it is more
-     * difficult for location providers to save power using the minDistance
-     * parameter, so minTime should be the primary tool to conserving battery
-     * life.
-     *
-     * <p> If your application wants to passively observe location
-     * updates triggered by other applications, but not consume
-     * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
-     * This provider does not actively turn on or modify active location
-     * providers, so you do not need to be as careful about minTime and
-     * minDistance. However if your application performs heavy work
-     * on a location update (such as network activity) then you should
-     * select non-zero values for minTime and/or minDistance to rate-limit
-     * your update frequency in the case another application enables a
-     * location provider with extremely fast updates.
-     *
-     * <p> The calling thread must be a {@link android.os.Looper} thread such as
-     * the main thread of the calling Activity.
-     *
-     * <p class="note"> Prior to Jellybean, the minTime parameter was
-     * only a hint, and some location provider implementations ignored it.
-     * From Jellybean and onwards it is mandatory for Android compatible
-     * devices to observe both the minTime and minDistance parameters.
+     * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+     * for more detail on how to use this (deprecated) method.
      *
      * @param provider the name of the provider with which to register
      * @param minTime minimum time interval between location updates, in milliseconds
@@ -497,8 +488,8 @@
      * on this device
      * @throws IllegalArgumentException if listener is null
      * @throws RuntimeException if the calling thread has no Looper
-     * @throws SecurityException if no suitable permission is present for the provider.
-     * @deprecated use the {@link LocationRequest} class instead
+     * @throws SecurityException if no suitable permission is present
+     * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
      */
     @Deprecated
     public void requestLocationUpdates(String provider, long minTime, float minDistance,
@@ -512,63 +503,11 @@
     }
 
     /**
-     * Registers the current activity to be notified periodically by
-     * the named provider.  Periodically, the supplied LocationListener will
-     * be called with the current Location or with status updates.
+     * Register for location updates using the named provider, and a callback on
+     * the specified looper thread.
      *
-     * <p> It may take a while to receive the first location update. If
-     * an immediate location is required, applications may use the
-     * {@link #getLastKnownLocation(String)} method.
-     *
-     * <p> In case the provider is disabled by the user, updates will stop,
-     * and the {@link LocationListener#onProviderDisabled(String)}
-     * method will be called. As soon as the provider is enabled again,
-     * the {@link LocationListener#onProviderEnabled(String)} method will
-     * be called and location updates will start again.
-     *
-     * <p> The update interval can be controlled using the minTime parameter.
-     * The elapsed time between location updates will never be less than
-     * minTime, although it can be more depending on the Location Provider
-     * implementation and the update interval requested by other applications.
-     *
-     * <p> Choosing a sensible value for minTime is important to conserve
-     * battery life. Each location update requires power from
-     * GPS, WIFI, Cell and other radios. Select a minTime value as high as
-     * possible while still providing a reasonable user experience.
-     * If your application is not in the foreground and showing
-     * location to the user then your application should avoid using an active
-     * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
-     * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
-     * or greater. If your application is in the foreground and showing
-     * location to the user then it is appropriate to select a faster
-     * update interval.
-     *
-     * <p> The minDistance parameter can also be used to control the
-     * frequency of location updates. If it is greater than 0 then the
-     * location provider will only send your application an update when
-     * the location has changed by at least minDistance meters, AND
-     * at least minTime milliseconds have passed. However it is more
-     * difficult for location providers to save power using the minDistance
-     * parameter, so minTime should be the primary tool to conserving battery
-     * life.
-     *
-     * <p> If your application wants to passively observe location
-     * updates triggered by other applications, but not consume
-     * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
-     * This provider does not actively turn on or modify active location
-     * providers, so you do not need to be as careful about minTime and
-     * minDistance. However if your application performs heavy work
-     * on a location update (such as network activity) then you should
-     * select non-zero values for minTime and/or minDistance to rate-limit
-     * your update frequency in the case another application enables a
-     * location provider with extremely fast updates.
-     *
-     * <p> The supplied Looper is used to implement the callback mechanism.
-     *
-     * <p class="note"> Prior to Jellybean, the minTime parameter was
-     * only a hint, and some location provider implementations ignored it.
-     * From Jellybean and onwards it is mandatory for Android compatible
-     * devices to observe both the minTime and minDistance parameters.
+     * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+     * for more detail on how to use this (deprecated) method.
      *
      * @param provider the name of the provider with which to register
      * @param minTime minimum time interval between location updates, in milliseconds
@@ -577,13 +516,14 @@
      * {@link LocationListener#onLocationChanged} method will be called for
      * each location update
      * @param looper a Looper object whose message queue will be used to
-     * implement the callback mechanism, or null to make callbacks on the
-     * main thread
+     * implement the callback mechanism, or null to make callbacks on the calling
+     * thread
      *
      * @throws IllegalArgumentException if provider is null or doesn't exist
      * @throws IllegalArgumentException if listener is null
-     * @throws SecurityException if no suitable permission is present for the provider.
-     * @deprecated use the {@link LocationRequest} class instead
+     * @throws SecurityException if no suitable permission is present
+     *
+     * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
      */
     @Deprecated
     public void requestLocationUpdates(String provider, long minTime, float minDistance,
@@ -597,52 +537,11 @@
     }
 
     /**
-     * Registers the current activity to be notified periodically based on
-     * the supplied criteria.  Periodically, the supplied LocationListener will
-     * be called with the current Location or with status updates.
+     * Register for location updates using a Criteria, and a callback
+     * on the specified looper thread.
      *
-     * <p> It may take a while to receive the first location update. If
-     * an immediate location is required, applications may use the
-     * {@link #getLastKnownLocation(String)} method.
-     *
-     * <p> In case the provider is disabled by the user, updates will stop,
-     * and the {@link LocationListener#onProviderDisabled(String)}
-     * method will be called. As soon as the provider is enabled again,
-     * the {@link LocationListener#onProviderEnabled(String)} method will
-     * be called and location updates will start again.
-     *
-     * <p> The update interval can be controlled using the minTime parameter.
-     * The elapsed time between location updates will never be less than
-     * minTime, although it can be more depending on the Location Provider
-     * implementation and the update interval requested by other applications.
-     *
-     * <p> Choosing a sensible value for minTime is important to conserve
-     * battery life. Each location update requires power from
-     * GPS, WIFI, Cell and other radios. Select a minTime value as high as
-     * possible while still providing a reasonable user experience.
-     * If your application is not in the foreground and showing
-     * location to the user then your application should avoid using an active
-     * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
-     * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
-     * or greater. If your application is in the foreground and showing
-     * location to the user then it is appropriate to select a faster
-     * update interval.
-     *
-     * <p> The minDistance parameter can also be used to control the
-     * frequency of location updates. If it is greater than 0 then the
-     * location provider will only send your application an update when
-     * the location has changed by at least minDistance meters, AND
-     * at least minTime milliseconds have passed. However it is more
-     * difficult for location providers to save power using the minDistance
-     * parameter, so minTime should be the primary tool to conserving battery
-     * life.
-     *
-     * <p> The supplied Looper is used to implement the callback mechanism.
-     *
-     * <p class="note"> Prior to Jellybean, the minTime parameter was
-     * only a hint, and some location provider implementations ignored it.
-     * From Jellybean and onwards it is mandatory for Android compatible
-     * devices to observe both the minTime and minDistance parameters.
+     * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+     * for more detail on how to use this (deprecated) method.
      *
      * @param minTime minimum time interval between location updates, in milliseconds
      * @param minDistance minimum distance between location updates, in meters
@@ -652,13 +551,14 @@
      * {@link LocationListener#onLocationChanged} method will be called for
      * each location update
      * @param looper a Looper object whose message queue will be used to
-     * implement the callback mechanism, or null to make callbacks on the
-     * main thread.
+     * implement the callback mechanism, or null to make callbacks on the calling
+     * thread
      *
      * @throws IllegalArgumentException if criteria is null
      * @throws IllegalArgumentException if listener is null
-     * @throws SecurityException if no suitable permission is present for the provider.
-     * @deprecated use the {@link LocationRequest} class instead
+     * @throws SecurityException if no suitable permission is present
+     *
+     * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
      */
     @Deprecated
     public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria,
@@ -672,70 +572,11 @@
     }
 
     /**
-     * Registers the current activity to be notified periodically by
-     * the named provider.  Periodically, the supplied PendingIntent will
-     * be broadcast with the current Location or with status updates.
+     * Register for location updates using the named provider, and a
+     * pending intent.
      *
-     * <p> Location updates are sent with a key of
-     * {@link #KEY_LOCATION_CHANGED} and a {@link android.location.Location} value.
-     *
-     * <p> It may take a while to receive the first location update. If
-     * an immediate location is required, applications may use the
-     * {@link #getLastKnownLocation(String)} method.
-     *
-     * <p> The update interval can be controlled using the minTime parameter.
-     * The elapsed time between location updates will never be less than
-     * minTime, although it can be more depending on the Location Provider
-     * implementation and the update interval requested by other applications.
-     *
-     * <p> Choosing a sensible value for minTime is important to conserve
-     * battery life. Each location update requires power from
-     * GPS, WIFI, Cell and other radios. Select a minTime value as high as
-     * possible while still providing a reasonable user experience.
-     * If your application is not in the foreground and showing
-     * location to the user then your application should avoid using an active
-     * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
-     * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
-     * or greater. If your application is in the foreground and showing
-     * location to the user then it is appropriate to select a faster
-     * update interval.
-     *
-     * <p> The minDistance parameter can also be used to control the
-     * frequency of location updates. If it is greater than 0 then the
-     * location provider will only send your application an update when
-     * the location has changed by at least minDistance meters, AND
-     * at least minTime milliseconds have passed. However it is more
-     * difficult for location providers to save power using the minDistance
-     * parameter, so minTime should be the primary tool to conserving battery
-     * life.
-     *
-     * <p> If your application wants to passively observe location
-     * updates triggered by other applications, but not consume
-     * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
-     * This provider does not actively turn on or modify active location
-     * providers, so you do not need to be as careful about minTime and
-     * minDistance. However if your application performs heavy work
-     * on a location update (such as network activity) then you should
-     * select non-zero values for minTime and/or minDistance to rate-limit
-     * your update frequency in the case another application enables a
-     * location provider with extremely fast updates.
-     *
-     * <p> If the provider is disabled by the user, updates will stop,
-     * and an intent will be sent with an extra with key
-     * {@link #KEY_PROVIDER_ENABLED} and a boolean value of false.
-     * If the provider is re-enabled, an intent will be sent with an
-     * extra with key {@link #KEY_PROVIDER_ENABLED} and a boolean value of
-     * true and location updates will start again.
-     *
-     * <p> If the provider's status changes, an intent will be sent with
-     * an extra with key {@link #KEY_STATUS_CHANGED} and an integer value
-     * indicating the new status.  Any extras associated with the status
-     * update will be sent as well.
-     *
-     * <p class="note"> Prior to Jellybean, the minTime parameter was
-     * only a hint, and some location provider implementations ignored it.
-     * From Jellybean and onwards it is mandatory for Android compatible
-     * devices to observe both the minTime and minDistance parameters.
+     * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+     * for more detail on how to use this (deprecated) method.
      *
      * @param provider the name of the provider with which to register
      * @param minTime minimum time interval between location updates, in milliseconds
@@ -745,8 +586,9 @@
      * @throws IllegalArgumentException if provider is null or doesn't exist
      * on this device
      * @throws IllegalArgumentException if intent is null
-     * @throws SecurityException if no suitable permission is present for the provider.
-     * @deprecated use the {@link LocationRequest} class instead
+     * @throws SecurityException if no suitable permission is present
+     *
+     * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
      */
     @Deprecated
     public void requestLocationUpdates(String provider, long minTime, float minDistance,
@@ -760,18 +602,28 @@
     }
 
     /**
-     * Registers the current activity to be notified periodically based on
-     * the supplied criteria.  Periodically, the supplied PendingIntent will
-     * be broadcast with the current Location or with status updates.
+     * Register for location updates using a Criteria and pending intent.
      *
-     * <p> Location updates are sent with a key of
-     * {@link #KEY_LOCATION_CHANGED} and a {@link android.location.Location} value.
+     * <p>The <code>requestLocationUpdates()</code> and
+     * <code>requestSingleUpdate()</code> methods involving
+     * an explicit String provider or {@link Criteria} are deprecated.
+     *
+     * <p>They register the current activity to be updated
+     * periodically by the named provider, or by the provider matching
+     * the specified {@link Criteria}, with location and status updates.
      *
      * <p> It may take a while to receive the first location update. If
      * an immediate location is required, applications may use the
      * {@link #getLastKnownLocation(String)} method.
      *
-     * <p> The update interval can be controlled using the minTime parameter.
+     * <p> Location updates are received either by {@link LocationListener}
+     * callbacks, or by broadcast intents to a supplied {@link PendingIntent}.
+     *
+     * <p> If the caller supplied a pending intent, then location updates
+     * are sent with a key of {@link #KEY_LOCATION_CHANGED} and a
+     * {@link android.location.Location} value.
+     *
+     * <p> The location update interval can be controlled using the minTime parameter.
      * The elapsed time between location updates will never be less than
      * minTime, although it can be more depending on the Location Provider
      * implementation and the update interval requested by other applications.
@@ -797,17 +649,36 @@
      * parameter, so minTime should be the primary tool to conserving battery
      * life.
      *
-     * <p> If the provider is disabled by the user, updates will stop,
-     * and an intent will be sent with an extra with key
-     * {@link #KEY_PROVIDER_ENABLED} and a boolean value of false.
-     * If the provider is re-enabled, an intent will be sent with an
-     * extra with key {@link #KEY_PROVIDER_ENABLED} and a boolean value of
-     * true and location updates will start again.
+     * <p> If your application wants to passively observe location
+     * updates triggered by other applications, but not consume
+     * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
+     * This provider does not actively turn on or modify active location
+     * providers, so you do not need to be as careful about minTime and
+     * minDistance. However if your application performs heavy work
+     * on a location update (such as network activity) then you should
+     * select non-zero values for minTime and/or minDistance to rate-limit
+     * your update frequency in the case another application enables a
+     * location provider with extremely fast updates.
      *
-     * <p> If the provider's status changes, an intent will be sent with
-     * an extra with key {@link #KEY_STATUS_CHANGED} and an integer value
-     * indicating the new status.  Any extras associated with the status
-     * update will be sent as well.
+     * <p>In case the provider is disabled by the user, updates will stop,
+     * and a provider availability update will be sent.
+     * As soon as the provider is enabled again,
+     * location updates will immediately resume and a provider availability
+     * update sent. Providers can also send status updates, at any time,
+     * with extra's specific to the provider. If a callback was supplied
+     * then status and availability updates are via
+     * {@link LocationListener#onProviderDisabled},
+     * {@link LocationListener#onProviderEnabled} or
+     * {@link LocationListener#onStatusChanged}. Alternately, if a
+     * pending intent was supplied then status and availability updates
+     * are broadcast intents with extra keys of
+     * {@link #KEY_PROVIDER_ENABLED} or {@link #KEY_STATUS_CHANGED}.
+     *
+     * <p> If a {@link LocationListener} is used but with no Looper specified
+     * then the calling thread must already
+     * be a {@link android.os.Looper} thread such as the main thread of the
+     * calling Activity. If a Looper is specified with a {@link LocationListener}
+     * then callbacks are made on the supplied Looper thread.
      *
      * <p class="note"> Prior to Jellybean, the minTime parameter was
      * only a hint, and some location provider implementations ignored it.
@@ -822,8 +693,9 @@
      *
      * @throws IllegalArgumentException if criteria is null
      * @throws IllegalArgumentException if intent is null
-     * @throws SecurityException if no suitable permission is present for the provider.
-     * @deprecated use the {@link LocationRequest} class instead
+     * @throws SecurityException if no suitable permission is present
+     *
+     * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
      */
     @Deprecated
     public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria,
@@ -837,32 +709,25 @@
     }
 
     /**
-     * Requests a single location update from the named provider.
+     * Register for a single location update using the named provider and
+     * a callback.
      *
-     * <p> It may take a while to receive the most recent location. If
-     * an immediate location is required, applications may use the
-     * {@link #getLastKnownLocation(String)} method.
-     *
-     * <p> In case the provider is disabled by the user, the update will not be received,
-     * and the {@link LocationListener#onProviderDisabled(String)}
-     * method will be called. As soon as the provider is enabled again,
-     * the {@link LocationListener#onProviderEnabled(String)} method will
-     * be called and location updates will start again.
-     *
-     * <p> The supplied Looper is used to implement the callback mechanism.
+     * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+     * for more detail on how to use this (deprecated) method.
      *
      * @param provider the name of the provider with which to register
      * @param listener a {#link LocationListener} whose
      * {@link LocationListener#onLocationChanged} method will be called when
      * the location update is available
      * @param looper a Looper object whose message queue will be used to
-     * implement the callback mechanism, or null to make callbacks on the
-     * main thread
+     * implement the callback mechanism, or null to make callbacks on the calling
+     * thread
      *
      * @throws IllegalArgumentException if provider is null or doesn't exist
      * @throws IllegalArgumentException if listener is null
-     * @throws SecurityException if no suitable permission is present for the provider
-     * @deprecated use the {@link LocationRequest} class instead
+     * @throws SecurityException if no suitable permission is present
+     *
+     * @deprecated Use {@link LocationRequest#setNumUpdates} instead
      */
     @Deprecated
     public void requestSingleUpdate(String provider, LocationListener listener, Looper looper) {
@@ -875,19 +740,11 @@
     }
 
     /**
-     * Requests a single location update based on the specified criteria.
+     * Register for a single location update using a Criteria and
+     * a callback.
      *
-     * <p> It may take a while to receive the most recent location. If
-     * an immediate location is required, applications may use the
-     * {@link #getLastKnownLocation(String)} method.
-     *
-     * <p> In case the provider is disabled by the user, the update will not be received,
-     * and the {@link LocationListener#onProviderDisabled(String)}
-     * method will be called. As soon as the provider is enabled again,
-     * the {@link LocationListener#onProviderEnabled(String)} method will
-     * be called and location updates will start again.
-     *
-     * <p> The supplied Looper is used to implement the callback mechanism.
+     * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+     * for more detail on how to use this (deprecated) method.
      *
      * @param criteria contains parameters for the location manager to choose the
      * appropriate provider and parameters to compute the location
@@ -895,14 +752,14 @@
      * {@link LocationListener#onLocationChanged} method will be called when
      * the location update is available
      * @param looper a Looper object whose message queue will be used to
-     * implement the callback mechanism, or null to make callbacks on the
-     * main thread
+     * implement the callback mechanism, or null to make callbacks on the calling
+     * thread
      *
      * @throws IllegalArgumentException if criteria is null
      * @throws IllegalArgumentException if listener is null
-     * @throws SecurityException if no suitable permission is present to access
-     * the location services
-     * @deprecated use the {@link LocationRequest} class instead
+     * @throws SecurityException if no suitable permission is present
+     *
+     * @deprecated Use {@link LocationRequest#setNumUpdates} instead
      */
     @Deprecated
     public void requestSingleUpdate(Criteria criteria, LocationListener listener, Looper looper) {
@@ -915,28 +772,19 @@
     }
 
     /**
-     * Requests a single location update from the named provider.
+     * Register for a single location update using a named provider and pending intent.
      *
-     * <p> It may take a while to receive the most recent location. If
-     * an immediate location is required, applications may use the
-     * {@link #getLastKnownLocation(String)} method.
-     *
-     * <p> Location updates are sent with a key of
-     * {@link #KEY_LOCATION_CHANGED} and a {@link android.location.Location} value.
-     *
-     * <p> In case the provider is disabled by the user, the update will not be received,
-     * and the {@link LocationListener#onProviderDisabled(String)}
-     * method will be called. As soon as the provider is enabled again,
-     * the {@link LocationListener#onProviderEnabled(String)} method will
-     * be called and location updates will start again.
+     * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+     * for more detail on how to use this (deprecated) method.
      *
      * @param provider the name of the provider with which to register
      * @param intent a {#link PendingIntent} to be sent for the location update
      *
      * @throws IllegalArgumentException if provider is null or doesn't exist
      * @throws IllegalArgumentException if intent is null
-     * @throws SecurityException if no suitable permission is present for the provider
-     * @deprecated use the {@link LocationRequest} class instead
+     * @throws SecurityException if no suitable permission is present
+     *
+     * @deprecated Use {@link LocationRequest#setNumUpdates} instead
      */
     @Deprecated
     public void requestSingleUpdate(String provider, PendingIntent intent) {
@@ -949,21 +797,10 @@
     }
 
     /**
-     * Requests a single location update based on the specified criteria.
+     * Register for a single location update using a Criteria and pending intent.
      *
-     * <p> It may take a while to receive the most recent location. If
-     * an immediate location is required, applications may use the
-     * {@link #getLastKnownLocation(String)} method.
-     *
-     * <p> Location updates are sent with a key of
-     * {@link #KEY_LOCATION_CHANGED} and a {@link android.location.Location} value.
-     *
-     * <p> If the provider is disabled by the user, an update will not be
-     * received, and an intent will be sent with an extra with key
-     * {@link #KEY_PROVIDER_ENABLED} and a boolean value of false.
-     * If the provider is re-enabled, an intent will be sent with an
-     * extra with key {@link #KEY_PROVIDER_ENABLED} and a boolean value of
-     * true and the location update will occur.
+     * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+     * for more detail on how to use this (deprecated) method.
      *
      * @param criteria contains parameters for the location manager to choose the
      * appropriate provider and parameters to compute the location
@@ -971,8 +808,9 @@
      *
      * @throws IllegalArgumentException if provider is null or doesn't exist
      * @throws IllegalArgumentException if intent is null
-     * @throws SecurityException if no suitable permission is present for the provider
-     * @deprecated use the {@link LocationRequest} class instead
+     * @throws SecurityException if no suitable permission is present
+     *
+     * @deprecated Use {@link LocationRequest#setNumUpdates} instead
      */
     @Deprecated
     public void requestSingleUpdate(Criteria criteria, PendingIntent intent) {
@@ -984,12 +822,74 @@
         requestLocationUpdates(request, null, null, intent);
     }
 
+    /**
+     * Register for fused location updates using a LocationRequest and callback.
+     *
+     * <p>The system will automatically select and enable the best providers
+     * to compute a location for your application. It may use only passive
+     * locations, or just a single location source, or it may fuse together
+     * multiple location sources in order to produce the best possible
+     * result, depending on the quality of service requested in the
+     * {@link LocationRequest}.
+     *
+     * <p>LocationRequest can be null, in which case the system will choose
+     * default, low power parameters for location updates. You will occasionally
+     * receive location updates as available, without a major power impact on the
+     * system. If your application just needs an occasional location update
+     * without any strict demands, then pass a null LocationRequest.
+     *
+     * <p>Only one LocationRequest can be registered for each unique callback
+     * or pending intent. So a subsequent request with the same callback or
+     * pending intent will over-write the previous LocationRequest.
+     *
+     * <p> If a pending intent is supplied then location updates
+     * are sent with a key of {@link #KEY_LOCATION_CHANGED} and a
+     * {@link android.location.Location} value. If a callback is supplied
+     * then location updates are made using the
+     * {@link LocationListener#onLocationChanged} callback, on the specified
+     * Looper thread. If a {@link LocationListener} is used
+     * but with a null Looper then the calling thread must already
+     * be a {@link android.os.Looper} thread (such as the main thread) and
+     * callbacks will occur on this thread.
+     *
+     * <p> Provider status updates and availability updates are deprecated
+     * because the system is performing provider fusion on the applications
+     * behalf. So {@link LocationListener#onProviderDisabled},
+     * {@link LocationListener#onProviderEnabled}, {@link LocationListener#onStatusChanged}
+     * will not be called, and intents with extra keys of
+     * {@link #KEY_PROVIDER_ENABLED} or {@link #KEY_STATUS_CHANGED} will not
+     * be received.
+     *
+     * @param request quality of service required, null for default low power
+     * @param listener a {#link LocationListener} whose
+     * {@link LocationListener#onLocationChanged} method will be called when
+     * the location update is available
+     * @param looper a Looper object whose message queue will be used to
+     * implement the callback mechanism, or null to make callbacks on the calling
+     * thread
+     *
+     * @throws IllegalArgumentException if listener is null
+     * @throws SecurityException if no suitable permission is present
+     */
     public void requestLocationUpdates(LocationRequest request, LocationListener listener,
             Looper looper) {
         checkListener(listener);
         requestLocationUpdates(request, listener, looper, null);
     }
 
+
+    /**
+     * Register for fused location updates using a LocationRequest and a pending intent.
+     *
+     * <p> See {@link #requestLocationUpdates(LocationRequest, LocationListener, Looper)}
+     * for more detail.
+     *
+     * @param request quality of service required, null for default low power
+     * @param intent a {#link PendingIntent} to be sent for the location update
+     *
+     * @throws IllegalArgumentException if intent is null
+     * @throws SecurityException if no suitable permission is present
+     */
     public void requestLocationUpdates(LocationRequest request, PendingIntent intent) {
         checkPendingIntent(intent);
         requestLocationUpdates(request, null, null, intent);
@@ -1023,11 +923,12 @@
     }
 
     /**
-     * Removes any current registration for location updates of the current activity
-     * with the given LocationListener.  Following this call, updates will no longer
+     * Removes all location updates for the specified LocationListener.
+     *
+     * <p>Following this call, updates will no longer
      * occur for this listener.
      *
-     * @param listener {#link LocationListener} object that no longer needs location updates
+     * @param listener listener object that no longer needs location updates
      * @throws IllegalArgumentException if listener is null
      */
     public void removeUpdates(LocationListener listener) {
@@ -1048,11 +949,11 @@
     }
 
     /**
-     * Removes any current registration for location updates of the current activity
-     * with the given PendingIntent.  Following this call, updates will no longer
-     * occur for this intent.
+     * Removes all location updates for the specified pending intent.
      *
-     * @param intent {#link PendingIntent} object that no longer needs location updates
+     * <p>Following this call, updates will no longer for this pending intent.
+     *
+     * @param intent pending intent object that no longer needs location updates
      * @throws IllegalArgumentException if intent is null
      */
     public void removeUpdates(PendingIntent intent) {
@@ -1067,8 +968,10 @@
     }
 
     /**
-     * Sets a proximity alert for the location given by the position
-     * (latitude, longitude) and the given radius.  When the device
+     * Set a proximity alert for the location given by the position
+     * (latitude, longitude) and the given radius.
+     *
+     * <p> When the device
      * detects that it has entered or exited the area surrounding the
      * location, the given PendingIntent will be used to create an Intent
      * to be fired.
@@ -1088,10 +991,6 @@
      * alert and no longer monitor it.  A value of -1 indicates that
      * there should be no expiration time.
      *
-     * <p> In case the screen goes to sleep, checks for proximity alerts
-     * happen only once every 4 minutes. This conserves battery life by
-     * ensuring that the device isn't perpetually awake.
-     *
      * <p> Internally, this method uses both {@link #NETWORK_PROVIDER}
      * and {@link #GPS_PROVIDER}.
      *
@@ -1106,9 +1005,9 @@
      * @param intent a PendingIntent that will be used to generate an Intent to
      * fire when entry to or exit from the alert region is detected
      *
-     * @throws SecurityException if no permission exists for the required
-     * providers.
-     * @deprecated use the {@link LocationRequest} class instead
+     * @throws SecurityException if no suitable permission is present
+     *
+     * @deprecated Use {@link LocationRequest} and {@link Geofence} instead
      */
     @Deprecated
     public void addProximityAlert(double latitude, double longitude, float radius, long expiration,
@@ -1125,7 +1024,40 @@
         }
     }
 
-    public void requestGeofence(LocationRequest request, Geofence fence, PendingIntent intent) {
+    /**
+     * Add a geofence with the specified LocationRequest quality of service.
+     *
+     * <p> When the device
+     * detects that it has entered or exited the area surrounding the
+     * location, the given PendingIntent will be used to create an Intent
+     * to be fired.
+     *
+     * <p> The fired Intent will have a boolean extra added with key
+     * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
+     * entering the proximity region; if false, it is exiting.
+     *
+     * <p> The geofence engine fuses results from all location providers to
+     * provide the best balance between accuracy and power. Applications
+     * can choose the quality of service required using the
+     * {@link LocationRequest} object. If it is null then a default,
+     * low power geo-fencing implementation is used. It is possible to cross
+     * a geo-fence without notification, but the system will do its best
+     * to detect, using {@link LocationRequest} as a hint to trade-off
+     * accuracy and power.
+     *
+     * <p> The power required by the geofence engine can depend on many factors,
+     * such as quality and interval requested in {@link LocationRequest},
+     * distance to nearest geofence and current device velocity.
+     *
+     * @param request quality of service required, null for default low power
+     * @param fence a geographical description of the geofence area
+     * @param intent pending intent to receive geofence updates
+     *
+     * @throws IllegalArgumentException if fence is null
+     * @throws IllegalArgumentException if intent is null
+     * @throws SecurityException if no suitable permission is present
+     */
+    public void addGeofence(LocationRequest request, Geofence fence, PendingIntent intent) {
         checkPendingIntent(intent);
         checkGeofence(fence);
 
@@ -1141,7 +1073,11 @@
      *
      * @param intent the PendingIntent that no longer needs to be notified of
      * proximity alerts
-     * @deprecated use the {@link LocationRequest} class instead
+     *
+     * @throws IllegalArgumentException if intent is null
+     * @throws SecurityException if no suitable permission is present
+     *
+     * @deprecated Use {@link LocationRequest} and {@link Geofence} instead
      */
     @Deprecated
     public void removeProximityAlert(PendingIntent intent) {
@@ -1155,6 +1091,19 @@
         }
     }
 
+    /**
+     * Remove a single geofence.
+     *
+     * <p>This removes only the specified geofence associated with the
+     * specified pending intent. All other geofences remain unchanged.
+     *
+     * @param fence a geofence previously passed to {@link #addGeofence}
+     * @param intent a pending intent previously passed to {@link #addGeofence}
+     *
+     * @throws IllegalArgumentException if fence is null
+     * @throws IllegalArgumentException if intent is null
+     * @throws SecurityException if no suitable permission is present
+     */
     public void removeGeofence(Geofence fence, PendingIntent intent) {
         checkPendingIntent(intent);
         checkGeofence(fence);
@@ -1167,6 +1116,14 @@
         }
     }
 
+    /**
+     * Remove all geofences registered to the specified pending intent.
+     *
+     * @param intent a pending intent previously passed to {@link #addGeofence}
+     *
+     * @throws IllegalArgumentException if intent is null
+     * @throws SecurityException if no suitable permission is present
+     */
     public void removeAllGeofences(PendingIntent intent) {
         checkPendingIntent(intent);
         String packageName = mContext.getPackageName();
@@ -1179,16 +1136,18 @@
     }
 
     /**
-     * Returns the current enabled/disabled status of the given provider. If the
-     * user has enabled this provider in the Settings menu, true is returned
-     * otherwise false is returned
+     * Returns the current enabled/disabled status of the given provider.
+     *
+     * <p>If the user has enabled this provider in the Settings menu, true
+     * is returned otherwise false is returned
      *
      * @param provider the name of the provider
      * @return true if the provider is enabled
      *
-     * @throws SecurityException if no suitable permission is present for the provider.
      * @throws IllegalArgumentException if provider is null
-     * @deprecated use the {@link LocationRequest} class instead
+     * @throws SecurityException if no suitable permission is present
+     *
+     * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
      */
     @Deprecated
     public boolean isProviderEnabled(String provider) {
@@ -1202,19 +1161,32 @@
         }
     }
 
-    public Location getLastLocation(LocationRequest request) {
+    /**
+     * Get the last known location.
+     *
+     * <p>This location could be very old so use
+     * {@link Location#getElapsedRealtimeNano} to calculate its age. It can
+     * also return null if no previous location is available.
+     *
+     * <p>Always returns immediately.
+     *
+     * @return The last known location, or null if not available
+     * @throws SecurityException if no suitable permission is present
+     */
+    public Location getLastLocation() {
         try {
-            return mService.getLastLocation(request);
+            return mService.getLastLocation(null);
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException", e);
             return null;
         }
     }
 
-
     /**
      * Returns a Location indicating the data from the last known
-     * location fix obtained from the given provider.  This can be done
+     * location fix obtained from the given provider.
+     *
+     * <p> This can be done
      * without starting the provider.  Note that this location could
      * be out-of-date, for example if the device was turned off and
      * moved to another location.
@@ -1224,9 +1196,10 @@
      * @param provider the name of the provider
      * @return the last known location for the provider, or null
      *
-     * @throws SecurityException if no suitable permission is present for the provider.
+     * @throws SecurityException if no suitable permission is present
      * @throws IllegalArgumentException if provider is null or doesn't exist
-     * @deprecated use the {@link LocationRequest} class instead
+     *
+     * @deprecated Use {@link #getLastLocation} instead
      */
     @Deprecated
     public Location getLastKnownLocation(String provider) {
@@ -1243,27 +1216,21 @@
         }
     }
 
-    // Mock provider support
+    // --- Mock provider support ---
+    // TODO: It would be fantastic to deprecate mock providers entirely, and replace
+    // with something closer to LocationProviderBase.java
 
     /**
      * Creates a mock location provider and adds it to the set of active providers.
      *
      * @param name the provider name
-     * @param requiresNetwork
-     * @param requiresSatellite
-     * @param requiresCell
-     * @param hasMonetaryCost
-     * @param supportsAltitude
-     * @param supportsSpeed
-     * @param supportsBearing
-     * @param powerRequirement
-     * @param accuracy
      *
      * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
      * Settings.Secure.ALLOW_MOCK_LOCATION} system setting is not enabled
      * @throws IllegalArgumentException if a provider with the given name already exists
-     * @deprecated use the {@link LocationRequest} class instead
+     *
+     * @deprecated requesting location providers by name is deprecated
      */
     @Deprecated
     public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
@@ -1292,7 +1259,8 @@
      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
      * @throws IllegalArgumentException if no provider with the given name exists
-     * @deprecated use the {@link LocationRequest} class instead
+     *
+     * @deprecated requesting location providers by name is deprecated
      */
     @Deprecated
     public void removeTestProvider(String provider) {
@@ -1318,7 +1286,8 @@
      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
      * @throws IllegalArgumentException if no provider with the given name exists
      * @throws IllegalArgumentException if the location is incomplete
-     * @deprecated use the {@link LocationRequest} class instead
+     *
+     * @deprecated requesting location providers by name is deprecated
      */
     @Deprecated
     public void setTestProviderLocation(String provider, Location loc) {
@@ -1351,7 +1320,8 @@
      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
      * @throws IllegalArgumentException if no provider with the given name exists
-     * @deprecated use the {@link LocationRequest} class instead
+     *
+     * @deprecated requesting location providers by name is deprecated
      */
     @Deprecated
     public void clearTestProviderLocation(String provider) {
@@ -1373,7 +1343,8 @@
      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
      * @throws IllegalArgumentException if no provider with the given name exists
-     * @deprecated use the {@link LocationRequest} class instead
+     *
+     * @deprecated requesting location providers by name is deprecated
      */
     @Deprecated
     public void setTestProviderEnabled(String provider, boolean enabled) {
@@ -1393,7 +1364,8 @@
      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
      * @throws IllegalArgumentException if no provider with the given name exists
-     * @deprecated use the {@link LocationRequest} class instead
+     *
+     * @deprecated requesting location providers by name is deprecated
      */
     @Deprecated
     public void clearTestProviderEnabled(String provider) {
@@ -1417,7 +1389,8 @@
      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
      * @throws IllegalArgumentException if no provider with the given name exists
-     * @deprecated use the {@link LocationRequest} class instead
+     *
+     * @deprecated requesting location providers by name is deprecated
      */
     @Deprecated
     public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
@@ -1437,7 +1410,8 @@
      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
      * @throws IllegalArgumentException if no provider with the given name exists
-     * @deprecated use the {@link LocationRequest} class instead
+     *
+     * @deprecated requesting location providers by name is deprecated
      */
     @Deprecated
     public void clearTestProviderStatus(String provider) {
@@ -1448,7 +1422,7 @@
         }
     }
 
-    // GPS-specific support
+    // --- GPS-specific support ---
 
     // This class is used to send GPS status events to the client's main thread.
     private class GpsStatusListenerTransport extends IGpsStatusListener.Stub {
@@ -1682,7 +1656,8 @@
      * The provider may optionally fill the extras Bundle with results from the command.
      *
      * @return true if the command succeeds.
-     * @deprecated use the {@link LocationRequest} class instead
+     *
+     * @deprecated Use {@link LocationRequest} instead, see notes on {@link LocationManager}
      */
     @Deprecated
     public boolean sendExtraCommand(String provider, String command, Bundle extras) {
@@ -1698,7 +1673,7 @@
      * Used by NetInitiatedActivity to report user response
      * for network initiated GPS fix requests.
      *
-     * {@hide}
+     * @hide
      */
     public boolean sendNiResponse(int notifId, int userResponse) {
     	try {
@@ -1720,6 +1695,7 @@
             throw new IllegalArgumentException("invalid criteria: " + criteria);
         }
     }
+
     private static void checkListener(LocationListener listener) {
         if (listener == null) {
             throw new IllegalArgumentException("invalid listener: " + listener);
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 3110196..b1863b8 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -21,24 +21,140 @@
 import android.os.SystemClock;
 import android.util.TimeUtils;
 
+
+/**
+ * A data object that contains quality of service parameters for requests
+ * to the {@link LocationManager}.
+ *
+ * <p>LocationRequest objects are used to request a quality of service
+ * for location updates from the Location Manager.
+ *
+ * <p>For example, if your application wants high accuracy location
+ * it should create a location request with {@link #setQuality} set to
+ * {@link #ACCURACY_FINE} or {@link #POWER_HIGH}, and it should set
+ * {@link #setInterval} to less than one second. This would be
+ * appropriate for mapping applications that are showing your location
+ * in real-time.
+ *
+ * <p>At the other extreme, if you want negligible power
+ * impact, but to still receive location updates when available, then use
+ * {@link #setQuality} with {@link #POWER_NONE}. With this request your
+ * application will not trigger (and therefore will not receive any
+ * power blame) any location updates, but will receive locations
+ * triggered by other applications. This would be appropriate for
+ * applications that have no firm requirement for location, but can
+ * take advantage when available.
+ *
+ * <p>In between these two extremes is a very common use-case, where
+ * applications definitely want to receive
+ * updates at a specified interval, and can receive them faster when
+ * available, but still want a low power impact. These applications
+ * should consider {@link #POWER_LOW} combined with a faster
+ * {@link #setFastestInterval} (such as 1 minute) and a slower
+ * {@link #setInterval} (such as 60 minutes). They will only be assigned
+ * power blame for the interval set by {@link #setInterval}, but can
+ * still receive locations triggered by other applications at a rate up
+ * to {@link #setFastestInterval}. This style of request is appropriate for
+ * many location aware applications, including background usage. Do be
+ * careful to also throttle {@link #setFastestInterval} if you perform
+ * heavy-weight work after receiving an update - such as using the network.
+ *
+ * <p>Activities should strongly consider removing all location
+ * request when entering the background
+ * (for example at {@link android.app.Activity#onPause}), or
+ * at least swap the request to a larger interval and lower quality.
+ * Future version of the location manager may automatically perform background
+ * throttling on behalf of applications.
+ *
+ * <p>Applications cannot specify the exact location sources that are
+ * used by Android's <em>Fusion Engine</em>. In fact, the system
+ * may have multiple location sources (providers) running and may
+ * fuse the results from several sources into a single Location object.
+ *
+ * <p>Location requests from applications with
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and not
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} will
+ * be automatically throttled to a slower interval, and the location
+ * object will be obfuscated to only show a coarse level of accuracy.
+ *
+ * <p>All location requests are considered hints, and you may receive
+ * locations that are more accurate, less accurate, and slower
+ * than requested.
+ */
 public final class LocationRequest implements Parcelable {
-    // QOS control
-    public static final int ACCURACY_FINE = 100;  // ~1 meter
-    public static final int ACCURACY_BLOCK = 102; // ~100 meters
-    public static final int ACCURACY_CITY = 104;  // ~10 km
+    /**
+     * Used with {@link #setQuality} to request the most accurate locations available.
+     *
+     * <p>This may be up to 1 meter accuracy, although this is implementation dependent.
+     */
+    public static final int ACCURACY_FINE = 100;
+
+    /**
+     * Used with {@link #setQuality} to request "block" level accuracy.
+     *
+     * <p>Block level accuracy is considered to be about 100 meter accuracy,
+     * although this is implementation dependent. Using a coarse accuracy
+     * such as this often consumes less power.
+     */
+    public static final int ACCURACY_BLOCK = 102;
+
+    /**
+     * Used with {@link #setQuality} to request "city" level accuracy.
+     *
+     * <p>City level accuracy is considered to be about 10km accuracy,
+     * although this is implementation dependent. Using a coarse accuracy
+     * such as this often consumes less power.
+     */
+    public static final int ACCURACY_CITY = 104;
+
+    /**
+     * Used with {@link #setQuality} to require no direct power impact (passive locations).
+     *
+     * <p>This location request will not trigger any active location requests,
+     * but will receive locations triggered by other applications. Your application
+     * will not receive any direct power blame for location work.
+     */
     public static final int POWER_NONE = 200;
+
+    /**
+     * Used with {@link #setQuality} to request low power impact.
+     *
+     * <p>This location request will avoid high power location work where
+     * possible.
+     */
     public static final int POWER_LOW = 201;
+
+    /**
+     * Used with {@link #setQuality} to allow high power consumption for location.
+     *
+     * <p>This location request will allow high power location work.
+     */
     public static final int POWER_HIGH = 203;
 
+    /**
+     * By default, mFastestInterval = FASTEST_INTERVAL_MULTIPLE * mInterval
+     */
+    private static final double FASTEST_INTERVAL_FACTOR = 6.0;  // 6x
+
     private int mQuality = POWER_LOW;
-    private long mFastestInterval = 6 * 1000;  // 6 seconds
-    private long mInterval = 60 * 1000;        // 1 minute
+    private long mInterval = 60 * 60 * 1000;   // 60 minutes
+    private long mFastestInterval = (long)(mInterval / FASTEST_INTERVAL_FACTOR);  // 10 minutes
+    private boolean mExplicitFastestInterval = false;
     private long mExpireAt = Long.MAX_VALUE;  // no expiry
     private int mNumUpdates = Integer.MAX_VALUE;  // no expiry
     private float mSmallestDisplacement = 0.0f;    // meters
 
     private String mProvider = null;  // for deprecated API's that explicitly request a provider
 
+    /**
+     * Create a location request with default parameters.
+     *
+     * <p>Default parameters are for a low power, slowly updated location.
+     * It can then be adjusted as required by the applications before passing
+     * to the {@link LocationManager}
+     *
+     * @return a new location request
+     */
     public static LocationRequest create() {
         LocationRequest request = new LocationRequest();
         return request;
@@ -105,52 +221,217 @@
     /** @hide */
     public LocationRequest() { }
 
+    /**
+     * Set the quality of the request.
+     *
+     * <p>Use with a accuracy constant such as {@link #ACCURACY_FINE}, or a power
+     * constant such as {@link #POWER_LOW}. You cannot request both and accuracy and
+     * power, only one or the other can be specified. The system will then
+     * maximize accuracy or minimize power as appropriate.
+     *
+     * <p>The quality of the request is a strong hint to the system for which
+     * location sources to use. For example, {@link #ACCURACY_FINE} is more likely
+     * to use GPS, and {@link #POWER_LOW} is more likely to use WIFI & Cell tower
+     * positioning, but it also depends on many other factors (such as which sources
+     * are available) and is implementation dependent.
+     *
+     * <p>{@link #setQuality} and {@link #setInterval} are the most important parameters
+     * on a location request.
+     *
+     * @param quality an accuracy or power constant
+     * @throws InvalidArgumentException if the quality constant is not valid
+     * @return the same object, so that setters can be chained
+     */
     public LocationRequest setQuality(int quality) {
         checkQuality(quality);
         mQuality = quality;
         return this;
     }
 
+    /**
+     * Get the quality of the request.
+     *
+     * @return an accuracy or power constant
+     */
     public int getQuality() {
         return mQuality;
     }
 
+    /**
+     * Set the desired interval for active location updates, in milliseconds.
+     *
+     * <p>The location manager will actively try to obtain location updates
+     * for your application at this interval, so it has a
+     * direct influence on the amount of power used by your application.
+     * Choose your interval wisely.
+     *
+     * <p>This interval is inexact. You may not receive updates at all (if
+     * no location sources are available), or you may receive them
+     * slower than requested. You may also receive them faster than
+     * requested (if other applications are requesting location at a
+     * faster interval). The fastest rate that that you will receive
+     * updates can be controlled with {@link #setFastestInterval}.
+     *
+     * <p>Applications with only the coarse location permission may have their
+     * interval silently throttled.
+     *
+     * <p>An interval of 0 is allowed, but not recommended, since
+     * location updates may be extremely fast on future implementations.
+     *
+     * <p>{@link #setQuality} and {@link #setInterval} are the most important parameters
+     * on a location request.
+     *
+     * @param millis desired interval in millisecond, inexact
+     * @throws InvalidArgumentException if the interval is less than zero
+     * @return the same object, so that setters can be chained
+     */
     public LocationRequest setInterval(long millis) {
         checkInterval(millis);
         mInterval = millis;
+        if (!mExplicitFastestInterval) {
+            mFastestInterval = (long)(mInterval / FASTEST_INTERVAL_FACTOR);
+        }
         return this;
     }
 
+    /**
+     * Get the desired interval of this request, in milliseconds.
+     *
+     * @return desired interval in milliseconds, inexact
+     */
     public long getInterval() {
         return mInterval;
     }
 
+    /**
+     * Explicitly set the fastest interval for location updates, in
+     * milliseconds.
+     *
+     * <p>This controls the fastest rate at which your application will
+     * receive location updates, which might be faster than
+     * {@link #setInterval} in some situations (for example, if other
+     * applications are triggering location updates).
+     *
+     * <p>This allows your application to passively acquire locations
+     * at a rate faster than it actively acquires locations, saving power.
+     *
+     * <p>Unlike {@link #setInterval}, this parameter is exact. Your
+     * application will never receive updates faster than this value.
+     *
+     * <p>If you don't call this method, a fastest interval
+     * will be selected for you. It will be a value faster than your
+     * active interval ({@link #setInterval}).
+     *
+     * <p>An interval of 0 is allowed, but not recommended, since
+     * location updates may be extremely fast on future implementations.
+     *
+     * <p>If {@link #setFastestInterval} is set slower than {@link #setInterval},
+     * then your effective fastest interval is {@link #setInterval}.
+     *
+     * @param millis fastest interval for updates in milliseconds, exact
+     * @throws InvalidArgumentException if the interval is less than zero
+     * @return the same object, so that setters can be chained
+     */
     public LocationRequest setFastestInterval(long millis) {
         checkInterval(millis);
+        mExplicitFastestInterval = true;
         mFastestInterval = millis;
         return this;
     }
 
+    /**
+     * Get the fastest interval of this request, in milliseconds.
+     *
+     * <p>The system will never provide location updates faster
+     * than the minimum of {@link #getFastestInterval} and
+     * {@link #getInterval}.
+     *
+     * @return fastest interval in milliseconds, exact
+     */
     public long getFastestInterval() {
         return mFastestInterval;
     }
 
+    /**
+     * Set the duration of this request, in milliseconds.
+     *
+     * <p>The duration begins immediately (and not when the request
+     * is passed to the location manager), so call this method again
+     * if the request is re-used at a later time.
+     *
+     * <p>The location manager will automatically stop updates after
+     * the request expires.
+     *
+     * <p>The duration includes suspend time. Values less than 0
+     * are allowed, but indicate that the request has already expired.
+     *
+     * @param millis duration of request in milliseconds
+     * @return the same object, so that setters can be chained
+     */
     public LocationRequest setExpireIn(long millis) {
         mExpireAt = millis + SystemClock.elapsedRealtime();
         if (mExpireAt < 0) mExpireAt = 0;
         return this;
     }
 
+    /**
+     * Set the request expiration time, in millisecond since boot.
+     *
+     * <p>This expiration time uses the same time base as {@link SystemClock#elapsedRealtime}.
+     *
+     * <p>The location manager will automatically stop updates after
+     * the request expires.
+     *
+     * <p>The duration includes suspend time. Values before {@link SystemClock#elapsedRealtime}
+     * are allowed,  but indicate that the request has already expired.
+     *
+     * @param millis expiration time of request, in milliseconds since boot including suspend
+     * @return the same object, so that setters can be chained
+     */
     public LocationRequest setExpireAt(long millis) {
         mExpireAt = millis;
         if (mExpireAt < 0) mExpireAt = 0;
         return this;
     }
 
+    /**
+     * Get the request expiration time, in milliseconds since boot.
+     *
+     * <p>This value can be compared to {@link SystemClock#elapsedRealtime} to determine
+     * the time until expiration.
+     *
+     * @return expiration time of request, in milliseconds since boot including suspend
+     */
     public long getExpireAt() {
         return mExpireAt;
     }
 
+    /**
+     * Set the number of location updates.
+     *
+     * <p>By default locations are continuously updated until the request is explicitly
+     * removed, however you can optionally request a set number of updates.
+     * For example, if your application only needs a single fresh location,
+     * then call this method with a value of 1 before passing the request
+     * to the location manager.
+     *
+     * @param numUpdates the number of location updates requested
+     * @throws InvalidArgumentException if numUpdates is 0 or less
+     * @return the same object, so that setters can be chained
+     */
+    public LocationRequest setNumUpdates(int numUpdates) {
+        if (numUpdates <= 0) throw new IllegalArgumentException("invalid numUpdates: " + numUpdates);
+        mNumUpdates = numUpdates;
+        return this;
+    }
+
+    /**
+     * Get the number of updates requested.
+     *
+     * <p>By default this is {@link Integer#MAX_VALUE}, which indicates that
+     * locations are updated until the request is explicitly removed.
+     * @return number of updates
+     */
     public int getNumUpdates() {
         return mNumUpdates;
     }
@@ -165,11 +446,6 @@
         }
     }
 
-    public LocationRequest setNumUpdates(int numUpdates) {
-        if (numUpdates < 0) throw new IllegalArgumentException("invalid numUpdates: " + numUpdates);
-        mNumUpdates = numUpdates;
-        return this;
-    }
 
     /** @hide */
     public LocationRequest setProvider(String provider) {
@@ -195,20 +471,6 @@
         return mSmallestDisplacement;
     }
 
-    /** @hide */
-    public LocationRequest applyCoarsePermissionRestrictions() {
-        switch (mQuality) {
-            case ACCURACY_FINE:
-                mQuality = ACCURACY_BLOCK;
-                break;
-        }
-        // cap fastest interval to 6 seconds
-        if (mFastestInterval < 6 * 1000) mFastestInterval = 6 * 1000;
-        // cap requested interval to 1 minute
-        if (mInterval < 60 * 1000) mInterval = 60 * 1000;
-        return this;
-    }
-
     private static void checkInterval(long millis) {
         if (millis < 0) {
             throw new IllegalArgumentException("invalid interval: " + millis);
@@ -261,10 +523,12 @@
             return new LocationRequest[size];
         }
     };
+
     @Override
     public int describeContents() {
         return 0;
     }
+
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeInt(mQuality);
diff --git a/location/lib/README.txt b/location/lib/README.txt
new file mode 100644
index 0000000..400a7dd
--- /dev/null
+++ b/location/lib/README.txt
@@ -0,0 +1,30 @@
+This library (com.android.location.provider.jar) is a shared java library
+containing classes required by unbundled location providers.
+
+--- Rules of this library ---
+o This library is effectively a PUBLIC API for unbundled location providers
+  that may be distributed outside the system image. So it MUST BE API STABLE.
+  You can add but not remove. The rules are the same as for the
+  public platform SDK API.
+o This library can see and instantiate internal platform classes (such as
+  ProviderRequest.java), but it must not expose them in any public method
+  (or by extending them via inheritance). This would break clients of the
+  library because they cannot see the internal platform classes.
+
+This library is distributed in the system image, and loaded as
+a shared library. So you can change the implementation, but not
+the interface. In this way it is like framework.jar.
+
+--- Why does this library exists? ---
+
+Unbundled location providers (such as the NetworkLocationProvider)
+can not use internal platform classes.
+
+So ideally all of these classes would be part of the public platform SDK API,
+but that doesn't seem like a great idea when only applications with a special
+signature can implement this API.
+
+The compromise is this library.
+
+It wraps internal platform classes (like ProviderRequest) with a stable
+API that does not leak the internal classes.
diff --git a/location/lib/java/com/android/location/provider/GeocodeProvider.java b/location/lib/java/com/android/location/provider/GeocodeProvider.java
index 666bb02..d7a34af 100644
--- a/location/lib/java/com/android/location/provider/GeocodeProvider.java
+++ b/location/lib/java/com/android/location/provider/GeocodeProvider.java
@@ -25,12 +25,14 @@
 import java.util.List;
 
 /**
- * An abstract superclass for geocode providers that are implemented
- * outside of the core android platform.
- * Geocode providers can be implemented as services and return the result of
+ * Base class for geocode providers implemented as unbundled services.
+ *
+ * <p>Geocode providers can be implemented as services and return the result of
  * {@link GeocodeProvider#getBinder()} in its getBinder() method.
  *
- * @hide
+ * <p>IMPORTANT: This class is effectively a public API for unbundled
+ * applications, and must remain API stable. See README.txt in the root
+ * of this package for more information.
  */
 public abstract class GeocodeProvider {
 
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index 53b0cae..b0e5d2c 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -34,10 +34,22 @@
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 
-
 /**
- * Base class for location providers implemented as services.
- * @hide
+ * Base class for location providers implemented as unbundled services.
+ *
+ * <p>The network location provider must export a service with action
+ * "com.android.location.service.v2.NetworkLocationProvider"
+ * and a valid minor version in a meta-data field on the service, and
+ * then return the result of {@link #getBinder()} on service binding.
+ *
+ * <p>The fused location provider must export a service with action
+ * "com.android.location.service.FusedLocationProvider"
+ * and a valid minor version in a meta-data field on the service, and
+ * then return the result of {@link #getBinder()} on service binding.
+ *
+ * <p>IMPORTANT: This class is effectively a public API for unbundled
+ * applications, and must remain API stable. See README.txt in the root
+ * of this package for more information.
  */
 public abstract class LocationProviderBase {
     private final String TAG;
diff --git a/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java b/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
index c8bdda4..9ee4df21 100644
--- a/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
@@ -19,9 +19,11 @@
 import com.android.internal.location.ProviderProperties;
 
 /**
- * This class is a public API for unbundled providers,
- * that hides the (hidden framework) ProviderProperties.
- * <p>Do _not_ remove public methods on this class.
+ * This class is an interface to Provider Properties for unbundled applications.
+ *
+ * <p>IMPORTANT: This class is effectively a public API for unbundled
+ * applications, and must remain API stable. See README.txt in the root
+ * of this package for more information.
  */
 public final class ProviderPropertiesUnbundled {
     private final ProviderProperties mProperties;
diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
index 3ff19ca..3605381 100644
--- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
@@ -23,9 +23,11 @@
 import com.android.internal.location.ProviderRequest;
 
 /**
- * This class is a public API for unbundled providers,
- * that hides the (hidden framework) ProviderRequest.
- * <p>Do _not_ remove public methods on this class.
+ * This class is an interface to Provider Requests for unbundled applications.
+ *
+ * <p>IMPORTANT: This class is effectively a public API for unbundled
+ * applications, and must remain API stable. See README.txt in the root
+ * of this package for more information.
  */
 public final class ProviderRequestUnbundled {
     private final ProviderRequest mRequest;
diff --git a/media/libdrm/mobile1/src/objmng/drm_api.c b/media/libdrm/mobile1/src/objmng/drm_api.c
index 249cdbe..232d9f4 100644
--- a/media/libdrm/mobile1/src/objmng/drm_api.c
+++ b/media/libdrm/mobile1/src/objmng/drm_api.c
@@ -1478,13 +1478,13 @@
 
     if (NULL != s->readBuf && s->readBufLen > 0) { /* read from backup buffer */
         if (leftLen <= s->readBufLen) {
-            memcpy(mediaBuf, s->readBuf + s->readBufOff, leftLen);
+            memcpy(mediaBuf + readBytes, s->readBuf + s->readBufOff, leftLen);
             s->readBufOff += leftLen;
             s->readBufLen -= leftLen;
             readBytes += leftLen;
             leftLen = 0;
         } else {
-            memcpy(mediaBuf, s->readBuf + s->readBufOff, s->readBufLen);
+            memcpy(mediaBuf + readBytes, s->readBuf + s->readBufOff, s->readBufLen);
             s->readBufOff += s->readBufLen;
             leftLen -= s->readBufLen;
             readBytes += s->readBufLen;
diff --git a/opengl/java/com/google/android/gles_jni/GLImpl.java b/opengl/java/com/google/android/gles_jni/GLImpl.java
index 07f9e91..6b23be9 100644
--- a/opengl/java/com/google/android/gles_jni/GLImpl.java
+++ b/opengl/java/com/google/android/gles_jni/GLImpl.java
@@ -23,7 +23,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.os.Build;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.nio.Buffer;
@@ -68,7 +68,7 @@
         int version = 0;
         IPackageManager pm = AppGlobals.getPackageManager();
         try {
-            ApplicationInfo applicationInfo = pm.getApplicationInfo(appName, 0, UserId.myUserId());
+            ApplicationInfo applicationInfo = pm.getApplicationInfo(appName, 0, UserHandle.myUserId());
             if (applicationInfo != null) {
                 version = applicationInfo.targetSdkVersion;
             }
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
index 38a6091..1c22c7a 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
@@ -22,7 +22,6 @@
 
 import com.android.location.provider.ProviderRequestUnbundled;
 
-
 import android.content.Context;
 import android.location.Location;
 import android.location.LocationListener;
diff --git a/packages/SettingsProvider/res/values-ms/defaults.xml b/packages/SettingsProvider/res/values-ms/defaults.xml
new file mode 100644
index 0000000..f8c3de3
--- /dev/null
+++ b/packages/SettingsProvider/res/values-ms/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_screensaver_component" msgid="2880543806753704722">"com.google.android.deskclock/com.android.deskclock.Screensaver"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-pt/defaults.xml b/packages/SettingsProvider/res/values-pt/defaults.xml
new file mode 100644
index 0000000..f8c3de3
--- /dev/null
+++ b/packages/SettingsProvider/res/values-pt/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_screensaver_component" msgid="2880543806753704722">"com.google.android.deskclock/com.android.deskclock.Screensaver"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-zh-rCN/defaults.xml b/packages/SettingsProvider/res/values-zh-rCN/defaults.xml
new file mode 100644
index 0000000..f8c3de3
--- /dev/null
+++ b/packages/SettingsProvider/res/values-zh-rCN/defaults.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, 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:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_screensaver_component" msgid="2880543806753704722">"com.google.android.deskclock/com.android.deskclock.Screensaver"</string>
+</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 4552a55..1481eb2 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -38,7 +38,6 @@
 import android.util.Log;
 
 import com.android.internal.content.PackageHelper;
-import com.android.internal.telephony.BaseCommands;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RILConstants;
@@ -65,7 +64,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 = 80;
+    private static final int DATABASE_VERSION = 81;
 
     private Context mContext;
 
@@ -1073,9 +1072,55 @@
             upgradeVersion = 79;
         }
 
+        if (upgradeVersion == 79) {
+            // Before touch exploration was a global setting controlled by the user
+            // via the UI. However, if the enabled accessibility services do not
+            // handle touch exploration mode, enabling it makes no sense. Therefore,
+            // now the services request touch exploration mode and the user is
+            // presented with a dialog to allow that and if she does we store that
+            // in the database. As a result of this change a user that has enabled
+            // accessibility, touch exploration, and some accessibility services
+            // may lose touch exploration state, thus rendering the device useless
+            // unless sighted help is provided, since the enabled service(s) are
+            // not in the list of services to which the user granted a permission
+            // to put the device in touch explore mode. Here we are allowing all
+            // enabled accessibility services to toggle touch exploration provided
+            // accessibility and touch exploration are enabled and no services can
+            // toggle touch exploration. Note that the user has already manually
+            // enabled the services and touch exploration which means the she has
+            // given consent to have these services work in touch exploration mode.
+            final boolean accessibilityEnabled = getIntValueFromTable(db, "secure",
+                    Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
+            final boolean touchExplorationEnabled = getIntValueFromTable(db, "secure",
+                    Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1;
+            if (accessibilityEnabled && touchExplorationEnabled) {
+                String enabledServices = getStringValueFromTable(db, "secure",
+                        Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
+                String touchExplorationGrantedServices = getStringValueFromTable(db, "secure",
+                        Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, "");
+                if (TextUtils.isEmpty(touchExplorationGrantedServices)
+                        && !TextUtils.isEmpty(enabledServices)) {
+                    SQLiteStatement stmt = null;
+                    try {
+                        db.beginTransaction();
+                        stmt = db.compileStatement("INSERT OR REPLACE INTO secure(name,value)"
+                                + " VALUES(?,?);");
+                        loadSetting(stmt,
+                                Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
+                                enabledServices);
+                        db.setTransactionSuccessful();
+                    } finally {
+                        db.endTransaction();
+                        if (stmt != null) stmt.close();
+                    }
+                }
+            }
+            upgradeVersion = 80;
+        }
+
         // vvv Jelly Bean MR1 changes begin here vvv
 
-        if (upgradeVersion == 79) {
+        if (upgradeVersion == 80) {
             // update screensaver settings
             db.beginTransaction();
             SQLiteStatement stmt = null;
@@ -1093,10 +1138,9 @@
                 db.endTransaction();
                 if (stmt != null) stmt.close();
             }
-            upgradeVersion = 80;
+            upgradeVersion = 81;
         }
 
-
         // *** Remember to update DATABASE_VERSION above!
 
         if (upgradeVersion != currentVersion) {
@@ -1743,18 +1787,28 @@
     }
 
     private int getIntValueFromSystem(SQLiteDatabase db, String name, int defaultValue) {
-        int value = defaultValue;
+        return getIntValueFromTable(db, "system", name, defaultValue);
+    }
+
+    private int getIntValueFromTable(SQLiteDatabase db, String table, String name,
+            int defaultValue) {
+        String value = getStringValueFromTable(db, table, name, null);
+        return (value != null) ? Integer.parseInt(value) : defaultValue;
+    }
+
+    private String getStringValueFromTable(SQLiteDatabase db, String table, String name,
+            String defaultValue) {
         Cursor c = null;
         try {
-            c = db.query("system", new String[] { Settings.System.VALUE }, "name='" + name + "'",
+            c = db.query(table, new String[] { Settings.System.VALUE }, "name='" + name + "'",
                     null, null, null, null);
             if (c != null && c.moveToFirst()) {
                 String val = c.getString(0);
-                value = val == null ? defaultValue : Integer.parseInt(val);
+                return val == null ? defaultValue : val;
             }
         } finally {
             if (c != null) c.close();
         }
-        return value;
+        return defaultValue;
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 18e7faa..9208cae 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -109,7 +109,7 @@
             IPowerManager power = IPowerManager.Stub.asInterface(
                     ServiceManager.getService("power"));
             if (power != null) {
-                power.setBacklightBrightness(brightness);
+                power.setTemporaryScreenBrightnessSettingOverride(brightness);
             }
         } catch (RemoteException doe) {
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 51dc3b1..e13378f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -15,6 +15,8 @@
     <uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
     <uses-permission android:name="android.permission.REMOTE_AUDIO_PLAYBACK" />
 
+    <uses-permission android:name="android.permission.MANAGE_USERS" />
+
     <!-- Networking and telephony -->
     <uses-permission android:name="android.permission.BLUETOOTH" />
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
diff --git a/packages/SystemUI/res/drawable-nodpi/qs_coming_soon.png b/packages/SystemUI/res/drawable-nodpi/qs_coming_soon.png
new file mode 100644
index 0000000..47c89b1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/qs_coming_soon.png
Binary files differ
diff --git a/packages/SystemUI/res/layout-sw600dp/super_status_bar.xml b/packages/SystemUI/res/layout-sw600dp/super_status_bar.xml
index c478334..b68632a 100644
--- a/packages/SystemUI/res/layout-sw600dp/super_status_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/super_status_bar.xml
@@ -31,10 +31,22 @@
         android:layout_height="@*android:dimen/status_bar_height"
         />
 
-    <include layout="@layout/status_bar_expanded"
-        android:layout_width="@dimen/notification_panel_width"
-        android:layout_height="0dp"
-        android:layout_gravity="center_horizontal|top"
-        />
 
+    <com.android.systemui.statusbar.phone.PanelHolder
+        android:id="@+id/panel_holder"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginTop="@*android:dimen/status_bar_height"
+        >
+        <include layout="@layout/status_bar_expanded"
+            android:layout_width="@dimen/notification_panel_width"
+            android:layout_height="wrap_content"
+            android:layout_gravity="left|top"
+            />
+        <include layout="@layout/quick_settings"
+            android:layout_width="@dimen/notification_panel_width"
+            android:layout_height="wrap_content"
+            android:layout_gravity="right|top"
+            />
+    </com.android.systemui.statusbar.phone.PanelHolder>
 </com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/SystemUI/res/layout/quick_settings.xml b/packages/SystemUI/res/layout/quick_settings.xml
new file mode 100644
index 0000000..c4b881e
--- /dev/null
+++ b/packages/SystemUI/res/layout/quick_settings.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<com.android.systemui.statusbar.phone.SettingsPanelView 
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:id="@+id/settings_panel"
+    >
+    <ImageView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:scaleType="centerInside"
+        android:src="@drawable/qs_coming_soon"
+        android:padding="4dp"
+        android:background="#80000080"
+        />
+    <LinearLayout android:id="@+id/handle"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/close_handle_height"
+        android:layout_gravity="bottom"
+        android:orientation="vertical"
+        >
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/close_handle_height"
+            android:layout_gravity="bottom"
+            android:scaleType="fitXY"
+            android:src="@drawable/status_bar_close"
+            />
+    </LinearLayout>
+</com.android.systemui.statusbar.phone.SettingsPanelView >
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 5841978..cb32d63 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -18,15 +18,16 @@
 */
 -->
 
-<FrameLayout 
+<com.android.systemui.statusbar.phone.NotificationPanelView 
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
     android:id="@+id/notification_panel"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
+    android:layout_width="0dp"
+    android:layout_height="wrap_content"
     android:background="@drawable/notification_panel_bg"
     android:paddingTop="@dimen/notification_panel_padding_top"
     android:layout_marginLeft="@dimen/notification_panel_margin_left"
+    android:animateLayoutChanges="true"
     >
 
     <TextView
@@ -42,9 +43,10 @@
 
     <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_height="wrap_content"
         android:layout_marginBottom="@dimen/close_handle_underlap"
         android:orientation="vertical"
+	    android:animateLayoutChanges="true"
         >
 
         <include layout="@layout/status_bar_expanded_header"
@@ -65,7 +67,7 @@
         <ScrollView
             android:id="@+id/scroll"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
+            android:layout_height="wrap_content"
             android:fadingEdge="none"
             android:overScrollMode="always"
             >
@@ -78,7 +80,7 @@
         </ScrollView>
     </LinearLayout>
 
-    <com.android.systemui.statusbar.phone.CloseDragHandle android:id="@+id/close"
+    <LinearLayout android:id="@+id/handle"
         android:layout_width="match_parent"
         android:layout_height="@dimen/close_handle_height"
         android:layout_gravity="bottom"
@@ -91,6 +93,5 @@
             android:scaleType="fitXY"
             android:src="@drawable/status_bar_close"
             />
-
-    </com.android.systemui.statusbar.phone.CloseDragHandle>
-</FrameLayout><!-- end of sliding panel -->
+    </LinearLayout>
+</com.android.systemui.statusbar.phone.NotificationPanelView><!-- end of sliding panel -->
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 7f598b6..60896c3 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -65,11 +65,24 @@
         android:layout_weight="1"
         />
 
+    <TextView
+        android:id="@+id/header_debug_info"
+        android:visibility="invisible"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:fontFamily="sans-serif-condensed"
+        android:textSize="11dp"
+        android:textStyle="bold"
+        android:textColor="#00A040"
+        android:padding="2dp"
+        />
+    
     <ImageView android:id="@+id/clear_all_button"
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:scaleType="center"
         android:src="@drawable/ic_notify_clear"
         android:contentDescription="@string/accessibility_clear_all"
-        />            
-</LinearLayout>
\ No newline at end of file
+        />     
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index dd70166..3dcdae8 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -49,4 +49,17 @@
         android:background="@drawable/bottom_divider_glow"
         />
 
+    <TextView
+        android:id="@+id/debug_info"
+        android:visibility="invisible"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom|right"
+        android:fontFamily="sans-serif-condensed"
+        android:textSize="9dp"
+        android:textStyle="bold"
+        android:textColor="#00A040"
+        android:padding="2dp"
+        />
+
 </FrameLayout>
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 5bf1a58..ad6b8f4 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -32,9 +32,20 @@
         android:layout_height="@*android:dimen/status_bar_height"
         />
 
-    <include layout="@layout/status_bar_expanded"
+    <com.android.systemui.statusbar.phone.PanelHolder
+        android:id="@+id/panel_holder"
         android:layout_width="match_parent"
-        android:layout_height="0dp"
-        />
+        android:layout_height="match_parent"
+        android:layout_marginTop="@*android:dimen/status_bar_height"
+        >
+        <include layout="@layout/status_bar_expanded"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            />
+        <include layout="@layout/quick_settings"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            />
+    </com.android.systemui.statusbar.phone.PanelHolder>
 
 </com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index afa0b20..c6c0719 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -19,8 +19,4 @@
     <!-- Layout parameters for the notification panel -->
 	<dimen name="notification_panel_margin_bottom">0dp</dimen>
     <dimen name="notification_panel_margin_left">32dp</dimen>
-
-    <!-- Gravity for the notification panel -->
-    <!-- 0x33 = left|top -->
-    <integer name="notification_panel_layout_gravity">0x33</integer>
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 2b5248f..b6faff3 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -21,11 +21,12 @@
 
     <!-- Layout parameters for the notification panel -->
     <dimen name="notification_panel_margin_bottom">192dp</dimen>
-    <dimen name="notification_panel_margin_left">0dp</dimen>
+    <dimen name="notification_panel_margin_left">16dp</dimen>
 
-    <!-- Gravity for the notification panel -->
-    <!-- 0x33 = center_horizontal|top -->
-    <integer name="notification_panel_layout_gravity">0x31</integer>
+    <!-- Gravity for the notification & quick settings panels -->
+    <!-- 0x33 = left|top ; 0x35 = right|top -->
+    <integer name="notification_panel_layout_gravity">0x33</integer>
+    <integer name="settings_panel_layout_gravity">0x35</integer>
 
     <!-- Diameter of outer shape drawable shown in navbar search-->
     <dimen name="navbar_search_outerring_diameter">430dip</dimen>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 9bbfc91..19b64ba 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -33,4 +33,5 @@
     <drawable name="system_bar_background">#ff000000</drawable>
     <!-- the darkening filter applied to notifications -->
     <drawable name="notification_icon_area_smoke">#aa000000</drawable>
+    <color name="notification_panel_scrim_color">#B0000000</color>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8204e95..9539373 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -158,9 +158,10 @@
     <dimen name="notification_panel_margin_bottom">0dp</dimen>
     <dimen name="notification_panel_margin_left">0dp</dimen>
 
-    <!-- Gravity for the notification panel -->
+    <!-- Gravity for the notification & quick settings panels -->
     <!-- 0x37 = fill_horizontal|top -->
     <integer name="notification_panel_layout_gravity">0x37</integer>
+    <integer name="settings_panel_layout_gravity">0x37</integer>
 
     <!-- Height of the carrier/wifi name label -->
     <dimen name="carrier_label_height">24dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
index 475fb6d..a2f43fd 100644
--- a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.os.UserHandle;
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.util.AttributeSet;
@@ -73,14 +74,15 @@
         // Close Recent Apps if needed
         mBar.animateCollapse(CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL);
         // Launch Assist
-        Intent intent = SearchManager.getAssistIntent(mContext);
+        Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+                .getAssistIntent(mContext, UserHandle.USER_CURRENT);
         if (intent == null) return;
         try {
             ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
                     R.anim.search_launch_enter, R.anim.search_launch_exit,
                     getHandler(), this);
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            mContext.startActivity(intent, opts.toBundle());
+            mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.USER_CURRENT);
         } catch (ActivityNotFoundException e) {
             Slog.w(TAG, "Activity not found for " + intent.getAction());
             onAnimationStarted();
@@ -140,7 +142,8 @@
     }
 
     private void maybeSwapSearchIcon() {
-        Intent intent = SearchManager.getAssistIntent(mContext);
+        Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+                .getAssistIntent(mContext, UserHandle.USER_CURRENT);
         if (intent != null) {
             ComponentName component = intent.getComponent();
             if (component == null || !mGlowPadView.replaceTargetDrawablesIfPresent(component,
@@ -277,6 +280,7 @@
     }
 
     public boolean isAssistantAvailable() {
-        return SearchManager.getAssistIntent(mContext) != null;
+        return ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+                .getAssistIntent(mContext, UserHandle.USER_CURRENT) != null;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
index 3e03f85..4d8c168 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -30,7 +30,7 @@
 import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Process;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.systemui.R;
@@ -245,7 +245,7 @@
 
                 final List<ActivityManager.RecentTaskInfo> recentTasks =
                         am.getRecentTasksForUser(MAX_TASKS,
-                                ActivityManager.RECENT_IGNORE_UNAVAILABLE, UserId.USER_CURRENT);
+                                ActivityManager.RECENT_IGNORE_UNAVAILABLE, UserHandle.USER_CURRENT);
                 int numTasks = recentTasks.size();
                 ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN)
                         .addCategory(Intent.CATEGORY_HOME).resolveActivityInfo(pm, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index bb647c3..7d36152 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -36,7 +36,7 @@
 import android.net.Uri;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -790,7 +790,7 @@
                     | Intent.FLAG_ACTIVITY_TASK_ON_HOME
                     | Intent.FLAG_ACTIVITY_NEW_TASK);
             if (DEBUG) Log.v(TAG, "Starting activity " + intent);
-            context.startActivityAsUser(intent, opts.toBundle(), UserId.USER_CURRENT);
+            context.startActivityAsUser(intent, opts.toBundle(), UserHandle.USER_CURRENT);
         }
         if (usingDrawingCache) {
             holder.thumbnailViewImage.setDrawingCacheEnabled(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index ea5089d..646f98a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -34,8 +34,10 @@
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.app.TaskStackBuilder;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.database.ContentObserver;
@@ -47,6 +49,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
@@ -65,6 +68,7 @@
 import android.widget.LinearLayout;
 import android.widget.PopupMenu;
 import android.widget.RemoteViews;
+import android.widget.TextView;
 
 import java.util.ArrayList;
 
@@ -72,6 +76,7 @@
     CommandQueue.Callbacks, RecentsPanelView.OnRecentsPanelVisibilityChangedListener {
     static final String TAG = "StatusBar";
     private static final boolean DEBUG = false;
+    public static final boolean MULTIUSER_DEBUG = false;
 
     protected static final int MSG_OPEN_RECENTS_PANEL = 1020;
     protected static final int MSG_CLOSE_RECENTS_PANEL = 1021;
@@ -112,6 +117,8 @@
 
     protected PopupMenu mNotificationBlamePopup;
 
+    protected int mCurrentUserId = 0;
+
     // UI-specific methods
 
     /**
@@ -252,6 +259,40 @@
                    switches[3]
                    ));
         }
+
+        // XXX: this is currently broken and will always return 0, but should start working at some point
+        try {
+            mCurrentUserId = ActivityManagerNative.getDefault().getCurrentUser().id;
+        } catch (RemoteException e) {
+            Log.v(TAG, "Couldn't get current user ID; guessing it's 0", e);
+        }
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_USER_SWITCHED);
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String action = intent.getAction();
+                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+                    mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+                    if (true) Slog.v(TAG, "userId " + mCurrentUserId + " is in the house");
+                    userSwitched(mCurrentUserId);
+                }
+            }}, filter);
+    }
+
+    public void userSwitched(int newUserId) {
+        // should be overridden
+    }
+
+    public boolean notificationIsForCurrentUser(StatusBarNotification n) {
+        final int thisUserId = mCurrentUserId;
+        final int notificationUserId = n.getUserId();
+        if (DEBUG && MULTIUSER_DEBUG) {
+            Slog.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
+                    n, thisUserId, notificationUserId));
+        }
+        return thisUserId == notificationUserId;
     }
 
     protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
@@ -604,6 +645,14 @@
         applyLegacyRowBackground(sbn, content);
 
         row.setTag(R.id.expandable_tag, Boolean.valueOf(large != null));
+
+        if (MULTIUSER_DEBUG) {
+            TextView debug = (TextView) row.findViewById(R.id.debug_info);
+            if (debug != null) {
+                debug.setVisibility(View.VISIBLE);
+                debug.setText("U " + entry.notification.getUserId());
+            }
+        }
         entry.row = row;
         entry.content = content;
         entry.expanded = expandedOneU;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
index 81a16ae..0f894a1 100755
--- a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
@@ -101,8 +101,7 @@
                 mDownTime = ev.getDownTime();
             } else {
                 if (mDownTime != ev.getDownTime()) {
-                    // TODO: remove
-                    throw new RuntimeException("Assertion failure in GestureRecorder: event downTime ("
+                    Slog.w(TAG, "Assertion failure in GestureRecorder: event downTime ("
                             +ev.getDownTime()+") does not match gesture downTime ("+mDownTime+")");
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
new file mode 100644
index 0000000..13a34ad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -0,0 +1,34 @@
+/*
+ * 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.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+public class NotificationPanelView extends PanelView {
+    public NotificationPanelView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void fling(float vel, boolean always) {
+        ((PhoneStatusBarView) mBar).mBar.getGestureRecorder().tag(
+            "fling " + ((vel > 0) ? "open" : "closed"),
+            "notifications,v=" + vel);
+        super.fling(vel, always);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
new file mode 100644
index 0000000..bffb903
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -0,0 +1,158 @@
+package com.android.systemui.statusbar.phone;
+
+import java.util.ArrayList;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.MotionEvent;
+import android.widget.FrameLayout;
+
+public class PanelBar extends FrameLayout {
+    public static final boolean DEBUG = false;
+    public static final String TAG = PanelView.class.getSimpleName();
+    public static final void LOG(String fmt, Object... args) {
+        if (!DEBUG) return;
+        Slog.v(TAG, String.format(fmt, args));
+    }
+
+    private PanelHolder mPanelHolder;
+    private ArrayList<PanelView> mPanels = new ArrayList<PanelView>();
+    protected PanelView mTouchingPanel;
+    private static final int STATE_CLOSED = 0;
+    private static final int STATE_TRANSITIONING = 1;
+    private static final int STATE_OPEN = 2;
+    private int mState = STATE_CLOSED;
+    private boolean mTracking;
+
+    private void go(int state) {
+        LOG("go state: %d -> %d", mState, state);
+        mState = state;
+    }
+
+    public PanelBar(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+    }
+
+    public void addPanel(PanelView pv) {
+        mPanels.add(pv);
+        pv.setBar(this);
+    }
+
+    public void setPanelHolder(PanelHolder ph) {
+        if (ph == null) {
+            Slog.e(TAG, "setPanelHolder: null PanelHolder", new Throwable());
+            return;
+        }
+        ph.setBar(this);
+        mPanelHolder = ph;
+        final int N = ph.getChildCount();
+        for (int i=0; i<N; i++) {
+            final PanelView v = (PanelView) ph.getChildAt(i);
+            if (v != null) {
+                addPanel(v);
+            }
+        }
+    }
+
+    public float getBarHeight() {
+        return getMeasuredHeight();
+    }
+
+    public PanelView selectPanelForTouchX(float x) {
+        final int N = mPanels.size();
+        return mPanels.get((int)(N * x / getMeasuredWidth()));
+    }
+
+    public boolean panelsEnabled() {
+        return true;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        // Allow subclasses to implement enable/disable semantics
+        if (!panelsEnabled()) return false;
+
+        // figure out which panel needs to be talked to here
+        if (event.getAction() == MotionEvent.ACTION_DOWN) {
+            mTouchingPanel = selectPanelForTouchX(event.getX());
+            mPanelHolder.setSelectedPanel(mTouchingPanel);
+            LOG("PanelBar.onTouch: state=%d ACTION_DOWN: panel %s", mState, mTouchingPanel.getName());
+            if (mState == STATE_CLOSED || mState == STATE_OPEN) {
+                go(STATE_TRANSITIONING);
+                onPanelPeeked();
+            }
+        }
+        final boolean result = mTouchingPanel.getHandle().dispatchTouchEvent(event);
+        return result;
+    }
+
+    public void panelExpansionChanged(PanelView panel, float frac) {
+        boolean fullyClosed = true;
+        PanelView fullyOpenedPanel = null;
+        LOG("panelExpansionChanged: start state=%d panel=%s", mState, panel.getName());
+        for (PanelView pv : mPanels) {
+            // adjust any other panels that may be partially visible
+            if (pv.getExpandedHeight() > 0f) {
+                fullyClosed = false;
+                final float thisFrac = pv.getExpandedFraction();
+                LOG("panelExpansionChanged:  -> %s: f=%.1f", pv.getName(), thisFrac);
+                if (panel == pv) {
+                    if (thisFrac == 1f) fullyOpenedPanel = panel;
+                } else {
+                    pv.setExpandedFraction(1f-frac);
+                }
+            }
+        }
+        if (fullyOpenedPanel != null && !mTracking) {
+            go(STATE_OPEN);
+            onPanelFullyOpened(fullyOpenedPanel);
+        } else if (fullyClosed && !mTracking) {
+            go(STATE_CLOSED);
+            onAllPanelsCollapsed();
+        }
+
+        LOG("panelExpansionChanged: end state=%d [%s%s ]", mState,
+                (fullyOpenedPanel!=null)?" fullyOpened":"", fullyClosed?" fullyClosed":"");
+    }
+
+    public void collapseAllPanels(boolean animate) {
+        for (PanelView pv : mPanels) {
+            if (animate && pv == mTouchingPanel) {
+                mTouchingPanel.collapse();
+            } else {
+                pv.setExpandedFraction(0); // just in case
+            }
+        }
+    }
+
+    public void onPanelPeeked() {
+        LOG("onPanelPeeked");
+    }
+
+    public void onAllPanelsCollapsed() {
+        LOG("onAllPanelsCollapsed");
+    }
+
+    public void onPanelFullyOpened(PanelView openPanel) {
+        LOG("onPanelFullyOpened");
+    }
+
+    public void onTrackingStarted(PanelView panel) {
+        mTracking = true;
+        if (panel != mTouchingPanel) {
+            LOG("shouldn't happen: onTrackingStarted(%s) != mTouchingPanel(%s)",
+                    panel, mTouchingPanel);
+        }
+    }
+
+    public void onTrackingStopped(PanelView panel) {
+        mTracking = false;
+        panelExpansionChanged(panel, panel.getExpandedFraction());
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java
new file mode 100644
index 0000000..abd82bd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java
@@ -0,0 +1,65 @@
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.FrameLayout;
+
+public class PanelHolder extends FrameLayout {
+
+    private int mSelectedPanelIndex;
+    private PanelBar mBar;
+
+    public PanelHolder(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setChildrenDrawingOrderEnabled(true);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        setChildrenDrawingOrderEnabled(true);
+    }
+
+    public int getPanelIndex(PanelView pv) {
+        final int N = getChildCount();
+        for (int i=0; i<N; i++) {
+            final PanelView v = (PanelView) getChildAt(i);
+            if (pv == v) return i;
+        }
+        return -1;
+    }
+
+    public void setSelectedPanel(PanelView pv) {
+        mSelectedPanelIndex = getPanelIndex(pv);
+    }
+
+    @Override
+    protected int getChildDrawingOrder(int childCount, int i) {
+        if (mSelectedPanelIndex == -1) {
+            return i;
+        } else {
+            if (i == childCount - 1) {
+                return mSelectedPanelIndex;
+            } else if (i >= mSelectedPanelIndex) {
+                return i + 1;
+            } else {
+                return i;
+            }
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mBar.collapseAllPanels(true);
+                break;
+        }
+        return false;
+    }
+
+    public void setBar(PanelBar panelBar) {
+        mBar = panelBar;
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
new file mode 100644
index 0000000..b595257
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -0,0 +1,364 @@
+package com.android.systemui.statusbar.phone;
+
+import android.animation.TimeAnimator;
+import android.animation.TimeAnimator.TimeListener;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.android.systemui.R;
+
+public class PanelView extends FrameLayout {
+    public static final boolean DEBUG = false;
+    public static final String TAG = PanelView.class.getSimpleName();
+    public final void LOG(String fmt, Object... args) {
+        if (!DEBUG) return;
+        Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
+    }
+
+    public static final boolean BRAKES = false;
+    private static final boolean STRETCH_PAST_CONTENTS = true;
+
+    private float mSelfExpandVelocityPx; // classic value: 2000px/s
+    private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
+    private float mFlingExpandMinVelocityPx; // classic value: 200px/s
+    private float mFlingCollapseMinVelocityPx; // classic value: 200px/s
+    private float mCollapseMinDisplayFraction; // classic value: 0.08 (25px/min(320px,480px) on G1)
+    private float mExpandMinDisplayFraction; // classic value: 0.5 (drag open halfway to expand)
+    private float mFlingGestureMaxXVelocityPx; // classic value: 150px/s
+
+    private float mExpandAccelPx; // classic value: 2000px/s/s
+    private float mCollapseAccelPx; // classic value: 2000px/s/s (will be negated to collapse "up")
+
+    private float mFlingGestureMaxOutputVelocityPx; // how fast can it really go? (should be a little
+                                                    // faster than mSelfCollapseVelocityPx)
+
+    private float mCollapseBrakingDistancePx = 200; // XXX Resource
+    private float mExpandBrakingDistancePx = 150; // XXX Resource
+    private float mBrakingSpeedPx = 150; // XXX Resource
+
+    private View mHandleView;
+    private float mTouchOffset;
+    private float mExpandedFraction = 0;
+    private float mExpandedHeight = 0;
+    private boolean mClosing;
+    private boolean mRubberbanding;
+    private boolean mTracking;
+
+    private TimeAnimator mTimeAnimator;
+    private VelocityTracker mVelocityTracker;
+
+    private int[] mAbsPos = new int[2];
+    PanelBar mBar;
+
+    private final TimeListener mAnimationCallback = new TimeListener() {
+        @Override
+        public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
+            animationTick(deltaTime);
+        }
+    };
+
+    private final Runnable mStopAnimator = new Runnable() { public void run() {
+        if (mTimeAnimator.isStarted()) {
+            mTimeAnimator.end();
+        }
+    }};
+
+    private float mVel, mAccel;
+    private int mFullHeight = 0;
+    private String mViewName;
+
+    private void animationTick(long dtms) {
+        if (!mTimeAnimator.isStarted()) {
+            // XXX HAX to work around bug in TimeAnimator.end() not resetting its last time
+            mTimeAnimator = new TimeAnimator();
+            mTimeAnimator.setTimeListener(mAnimationCallback);
+
+            mTimeAnimator.start();
+            
+            mRubberbanding = STRETCH_PAST_CONTENTS && mExpandedHeight > getFullHeight();
+            mClosing = (mExpandedHeight > 0 && mVel < 0) || mRubberbanding;
+        } else if (dtms > 0) {
+            final float dt = dtms * 0.001f;                  // ms -> s
+            LOG("tick: v=%.2fpx/s dt=%.4fs", mVel, dt);
+            LOG("tick: before: h=%d", (int) mExpandedHeight);
+
+            final float fh = getFullHeight();
+            boolean braking = false;
+            if (BRAKES) {
+                if (mClosing) {
+                    braking = mExpandedHeight <= mCollapseBrakingDistancePx;
+                    mAccel = braking ? 10*mCollapseAccelPx : -mCollapseAccelPx;
+                } else {
+                    braking = mExpandedHeight >= (fh-mExpandBrakingDistancePx);
+                    mAccel = braking ? 10*-mExpandAccelPx : mExpandAccelPx;
+                }
+            } else {
+                mAccel = mClosing ? -mCollapseAccelPx : mExpandAccelPx;
+            }
+
+            mVel += mAccel * dt;
+
+            if (braking) {
+                if (mClosing && mVel > -mBrakingSpeedPx) {
+                    mVel = -mBrakingSpeedPx;
+                } else if (!mClosing && mVel < mBrakingSpeedPx) {
+                    mVel = mBrakingSpeedPx;
+                }
+            } else {
+                if (mClosing && mVel > -mFlingCollapseMinVelocityPx) {
+                    mVel = -mFlingCollapseMinVelocityPx;
+                } else if (!mClosing && mVel > mFlingGestureMaxOutputVelocityPx) {
+                    mVel = mFlingGestureMaxOutputVelocityPx;
+                }
+            }
+
+            float h = mExpandedHeight + mVel * dt;
+            
+            if (mRubberbanding && h < fh) {
+                h = fh;
+            }
+
+            LOG("tick: new h=%d closing=%s", (int) h, mClosing?"true":"false");
+
+            setExpandedHeightInternal(h);
+
+            mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
+
+            if (mVel == 0
+                    || (mClosing && mExpandedHeight == 0)
+                    || ((mRubberbanding || !mClosing) && mExpandedHeight == fh)) {
+                post(mStopAnimator);
+            }
+        }
+    }
+
+    public PanelView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        mTimeAnimator = new TimeAnimator();
+        mTimeAnimator.setTimeListener(mAnimationCallback);
+    }
+
+    private void loadDimens() {
+        final Resources res = getContext().getResources();
+
+        mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity);
+        mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity);
+        mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity);
+        mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity);
+
+        mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1);
+        mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1);
+
+        mExpandAccelPx = res.getDimension(R.dimen.expand_accel);
+        mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel);
+
+        mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity);
+
+        mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity);
+    }
+
+    private void trackMovement(MotionEvent event) {
+        // Add movement to velocity tracker using raw screen X and Y coordinates instead
+        // of window coordinates because the window frame may be moving at the same time.
+        float deltaX = event.getRawX() - event.getX();
+        float deltaY = event.getRawY() - event.getY();
+        event.offsetLocation(deltaX, deltaY);
+        mVelocityTracker.addMovement(event);
+        event.offsetLocation(-deltaX, -deltaY);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        loadDimens();
+
+        mHandleView = findViewById(R.id.handle);
+        LOG("handle view: " + mHandleView);
+        if (mHandleView != null) {
+            mHandleView.setOnTouchListener(new View.OnTouchListener() {
+                @Override
+                public boolean onTouch(View v, MotionEvent event) {
+                    final float y = event.getY();
+                    final float rawY = event.getRawY();
+                    LOG("handle.onTouch: a=%s y=%.1f rawY=%.1f off=%.1f",
+                            MotionEvent.actionToString(event.getAction()),
+                            y, rawY, mTouchOffset);
+                    PanelView.this.getLocationOnScreen(mAbsPos);
+
+                    switch (event.getAction()) {
+                        case MotionEvent.ACTION_DOWN:
+                            mTracking = true;
+                            mVelocityTracker = VelocityTracker.obtain();
+                            trackMovement(event);
+                            mBar.onTrackingStarted(PanelView.this);
+                            mTouchOffset = (rawY - mAbsPos[1]) - PanelView.this.getExpandedHeight();
+                            break;
+
+                        case MotionEvent.ACTION_MOVE:
+                            PanelView.this.setExpandedHeightInternal(rawY - mAbsPos[1] - mTouchOffset);
+
+                            mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
+
+                            trackMovement(event);
+                            break;
+
+                        case MotionEvent.ACTION_UP:
+                        case MotionEvent.ACTION_CANCEL:
+                            mTracking = false;
+                            mBar.onTrackingStopped(PanelView.this);
+                            trackMovement(event);
+                            mVelocityTracker.computeCurrentVelocity(1000);
+
+                            float yVel = mVelocityTracker.getYVelocity();
+                            boolean negative = yVel < 0;
+
+                            float xVel = mVelocityTracker.getXVelocity();
+                            if (xVel < 0) {
+                                xVel = -xVel;
+                            }
+                            if (xVel > mFlingGestureMaxXVelocityPx) {
+                                xVel = mFlingGestureMaxXVelocityPx; // limit how much we care about the x axis
+                            }
+
+                            float vel = (float)Math.hypot(yVel, xVel);
+                            if (vel > mFlingGestureMaxOutputVelocityPx) {
+                                vel = mFlingGestureMaxOutputVelocityPx;
+                            }
+                            if (negative) {
+                                vel = -vel;
+                            }
+
+                            LOG("gesture: vraw=(%f,%f) vnorm=(%f,%f) vlinear=%f",
+                                    mVelocityTracker.getXVelocity(),
+                                    mVelocityTracker.getYVelocity(),
+                                    xVel, yVel,
+                                    vel);
+
+                            fling(vel, true);
+
+                            mVelocityTracker.recycle();
+                            mVelocityTracker = null;
+
+                            break;
+                    }
+                    return true;
+                }});
+        }
+    }
+
+    public void fling(float vel, boolean always) {
+        mVel = vel;
+
+        if (always||mVel != 0) {
+            animationTick(0); // begin the animation
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mViewName = getResources().getResourceName(getId());
+    }
+
+    public String getName() {
+        return mViewName;
+    }
+
+    @Override
+    protected void onViewAdded(View child) {
+        LOG("onViewAdded: " + child);
+    }
+
+    public View getHandle() {
+        return mHandleView;
+    }
+
+    // Rubberbands the panel to hold its contents.
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        LOG("onMeasure(%d, %d) -> (%d, %d)",
+                widthMeasureSpec, heightMeasureSpec, getMeasuredWidth(), getMeasuredHeight());
+        mFullHeight = getMeasuredHeight();
+        // if one of our children is getting smaller, we should track that
+        if (!mTracking && !mRubberbanding && !mTimeAnimator.isStarted() && mExpandedHeight > 0 && mExpandedHeight != mFullHeight) {
+            mExpandedHeight = mFullHeight;
+        }
+        heightMeasureSpec = MeasureSpec.makeMeasureSpec(
+                    (int) mExpandedHeight, MeasureSpec.AT_MOST); // MeasureSpec.getMode(heightMeasureSpec));
+        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
+    }
+
+
+    public void setExpandedHeight(float height) {
+        post(mStopAnimator);
+        setExpandedHeightInternal(height);
+    }
+
+    @Override
+    protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
+        LOG("onLayout: changed=%s, bottom=%d eh=%d fh=%d", changed?"T":"f", bottom, (int)mExpandedHeight, (int)mFullHeight);
+        super.onLayout(changed, left, top, right, bottom);
+    }
+
+    public void setExpandedHeightInternal(float h) {
+        float fh = getFullHeight();
+        if (fh == 0) {
+            // Hmm, full height hasn't been computed yet
+        }
+
+        LOG("setExpansion: height=%.1f fh=%.1f tracking=%s rubber=%s", h, fh, mTracking?"T":"f", mRubberbanding?"T":"f");
+
+        if (h < 0) h = 0;
+        if (!(STRETCH_PAST_CONTENTS && (mTracking || mRubberbanding)) && h > fh) h = fh;
+        mExpandedHeight = h;
+
+        requestLayout();
+//        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+//        lp.height = (int) mExpandedHeight;
+//        setLayoutParams(lp);
+
+        mExpandedFraction = Math.min(1f, h / fh);
+    }
+
+    private float getFullHeight() {
+        return mFullHeight;
+    }
+
+    public void setExpandedFraction(float frac) {
+        setExpandedHeight(getFullHeight() * frac);
+    }
+
+    public float getExpandedHeight() {
+        return mExpandedHeight;
+    }
+
+    public float getExpandedFraction() {
+        return mExpandedFraction;
+    }
+
+    public void setBar(PanelBar panelBar) {
+        mBar = panelBar;
+    }
+
+    public void collapse() {
+        // TODO: abort animation or ongoing touch
+        if (mExpandedHeight > 0) {
+            fling(-mSelfCollapseVelocityPx, /*always=*/ true);
+        }
+    }
+
+    public void expand() {
+        if (mExpandedHeight < getFullHeight()) {
+            fling (mSelfExpandVelocityPx, /*always=*/ true);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 0ccc415..a20576f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -46,13 +46,12 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.dreams.IDreamManager;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Slog;
-import android.view.Choreographer;
 import android.view.Display;
 import android.view.Gravity;
 import android.view.IWindowManager;
@@ -77,7 +76,6 @@
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.statusbar.StatusBarNotification;
 import com.android.systemui.R;
-import com.android.systemui.UniverseBackground;
 import com.android.systemui.recent.RecentTasksLoader;
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.CommandQueue;
@@ -111,8 +109,7 @@
     public static final String ACTION_STATUSBAR_START
             = "com.android.internal.policy.statusbar.START";
 
-    private static final boolean DIM_BEHIND_EXPANDED_PANEL = true;
-    private static final boolean SHOW_CARRIER_LABEL = true;
+    private static final boolean SHOW_CARRIER_LABEL = false; // XXX: doesn't work with rubberband panels right now
 
     private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
     private static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
@@ -159,8 +156,6 @@
     StatusBarWindowView mStatusBarWindow;
     PhoneStatusBarView mStatusBarView;
 
-    UniverseBackground mUniverseBackground;
-
     int mPixelFormat;
     Object mQueueLock = new Object();
 
@@ -171,14 +166,19 @@
     LinearLayout mStatusIcons;
 
     // expanded notifications
-    View mNotificationPanel; // the sliding/resizing panel within the notification window
+    PanelView mNotificationPanel; // the sliding/resizing panel within the notification window
     ScrollView mScrollView;
     View mExpandedContents;
-    int mNotificationPanelMarginBottomPx, mNotificationPanelMarginLeftPx;
     final Rect mNotificationPanelBackgroundPadding = new Rect();
     int mNotificationPanelGravity;
+    int mNotificationPanelMarginBottomPx, mNotificationPanelMarginPx;
     int mNotificationPanelMinHeight;
     boolean mNotificationPanelIsFullScreenWidth;
+    TextView mNotificationPanelDebugText;
+
+    // settings
+    PanelView mSettingsPanel;
+    int mSettingsPanelGravity;
 
     // top bar
     View mClearButton;
@@ -191,13 +191,8 @@
     private int mCarrierLabelHeight;
     private TextView mEmergencyCallLabel;
 
-    // drag bar
-    CloseDragHandle mCloseView;
-    private int mCloseViewHeight;
-
     // position
     int[] mPositionTmp = new int[2];
-    boolean mExpanded;
     boolean mExpandedVisible;
 
     // the date view
@@ -222,7 +217,6 @@
     boolean mTracking;
     VelocityTracker mVelocityTracker;
 
-    Choreographer mChoreographer;
     boolean mAnimating;
     boolean mClosing; // only valid when mAnimating; indicates the initial acceleration
     float mAnimY;
@@ -262,40 +256,6 @@
         }
     };
 
-    private final Runnable mStartRevealAnimation = new Runnable() {
-        @Override
-        public void run() {
-            mAnimAccel = mExpandAccelPx;
-            mAnimVel = mFlingExpandMinVelocityPx;
-            mAnimY = getStatusBarHeight();
-            updateExpandedViewPos((int)mAnimY);
-
-            mAnimating = true;
-            mAnimatingReveal = true;
-            resetLastAnimTime();
-            mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
-                mAnimationCallback, null);
-            mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
-                mRevealAnimationCallback, null);
-            mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
-                mRevealAnimationCallback, null);
-        }
-    };
-
-    private final Runnable mPerformSelfExpandFling = new Runnable() {
-        @Override
-        public void run() {
-            performFling(0, mSelfExpandVelocityPx, true);
-        }
-    };
-
-    private final Runnable mPerformFling = new Runnable() {
-        @Override
-        public void run() {
-            performFling(mFlingY + mViewDelta, mFlingVelocity, false);
-        }
-    };
-
     @Override
     public void start() {
         mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
@@ -313,11 +273,6 @@
 
         if (ENABLE_INTRUDERS) addIntruderView();
 
-        mUniverseBackground = new UniverseBackground(mContext);
-        mUniverseBackground.setVisibility(View.GONE);
-        WindowManagerImpl.getDefault().addView(mUniverseBackground,
-                mUniverseBackground.getLayoutParams(mDisplay));
-
         // Lastly, call to the icon policy to install/update all the icons.
         mIconPolicy = new PhoneStatusBarPolicy(mContext);
     }
@@ -345,7 +300,7 @@
             @Override
             public boolean onTouch(View v, MotionEvent event) {
                 if (event.getAction() == MotionEvent.ACTION_DOWN) {
-                    if (mExpanded && !mAnimating) {
+                    if (mExpandedVisible && !mAnimating) {
                         animateCollapse();
                     }
                 }
@@ -353,37 +308,49 @@
             }});
 
         mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
-        mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
+        mStatusBarView.setBar(this);
+
+        PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
+        mStatusBarView.setPanelHolder(holder);
+
+        mNotificationPanel = (PanelView) mStatusBarWindow.findViewById(R.id.notification_panel);
         // don't allow clicks on the panel to pass through to the background where they will cause the panel to close
-        mNotificationPanel.setOnTouchListener(new View.OnTouchListener() {
+        View.OnTouchListener clickStopper = new View.OnTouchListener() {
             @Override
             public boolean onTouch(View v, MotionEvent event) {
                 return true;
             }
-        });
+        };
+        mNotificationPanel.setOnTouchListener(clickStopper);
         mNotificationPanelIsFullScreenWidth =
             (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT);
         mNotificationPanel.setSystemUiVisibility(
                   View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER
                 | (mNotificationPanelIsFullScreenWidth ? 0 : View.STATUS_BAR_DISABLE_SYSTEM_INFO));
 
+        // quick settings (WIP)
+        mSettingsPanel = (PanelView) mStatusBarWindow.findViewById(R.id.settings_panel);
+        mSettingsPanel.setOnTouchListener(clickStopper);
+
         if (!ActivityManager.isHighEndGfx(mDisplay)) {
             mStatusBarWindow.setBackground(null);
             mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
                     R.color.notification_panel_solid_background)));
+            mSettingsPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
+                    R.color.notification_panel_solid_background)));
         }
         if (ENABLE_INTRUDERS) {
             mIntruderAlertView = (IntruderAlertView) View.inflate(context, R.layout.intruder_alert, null);
             mIntruderAlertView.setVisibility(View.GONE);
             mIntruderAlertView.setBar(this);
         }
+        if (MULTIUSER_DEBUG) {
+            mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(R.id.header_debug_info);
+            mNotificationPanelDebugText.setVisibility(View.VISIBLE);
+        }
 
         updateShowSearchHoldoff();
 
-        mStatusBarView.mService = this;
-
-        mChoreographer = Choreographer.getInstance();
-
         try {
             boolean showNav = mWindowManager.hasNavigationBar();
             if (DEBUG) Slog.v(TAG, "hasNavigationBar=" + showNav);
@@ -429,10 +396,6 @@
         TickerView tickerView = (TickerView)mStatusBarView.findViewById(R.id.tickerText);
         tickerView.mTicker = mTicker;
 
-        mCloseView = (CloseDragHandle)mStatusBarWindow.findViewById(R.id.close);
-        mCloseView.mService = this;
-        mCloseViewHeight = res.getDimensionPixelSize(R.dimen.close_handle_height);
-
         mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
 
         // set the inital view visibility
@@ -597,10 +560,6 @@
         return mNaturalBarHeight;
     }
 
-    private int getCloseViewHeight() {
-        return mCloseViewHeight;
-    }
-
     private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
         public void onClick(View v) {
             toggleRecentApps();
@@ -852,9 +811,9 @@
         // If the device hasn't been through Setup, we only show system notifications
         for (int i=0; i<N; i++) {
             Entry ent = mNotificationData.get(N-i-1);
-            if (provisioned || showNotificationEvenIfUnprovisioned(ent.notification)) {
-                toShow.add(ent.row);
-            }
+            if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
+            if (!notificationIsForCurrentUser(ent.notification)) continue;
+            toShow.add(ent.row);
         }
 
         ArrayList<View> toRemove = new ArrayList<View>();
@@ -900,10 +859,10 @@
         // If the device hasn't been through Setup, we only show system notifications
         for (int i=0; i<N; i++) {
             Entry ent = mNotificationData.get(N-i-1);
-            if ((provisioned && ent.notification.score >= HIDE_ICONS_BELOW_SCORE)
-                    || showNotificationEvenIfUnprovisioned(ent.notification)) {
-                toShow.add(ent.icon);
-            }
+            if (!((provisioned && ent.notification.score >= HIDE_ICONS_BELOW_SCORE)
+                    || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
+            if (!notificationIsForCurrentUser(ent.notification)) continue;
+            toShow.add(ent.icon);
         }
 
         ArrayList<View> toRemove = new ArrayList<View>();
@@ -1169,20 +1128,6 @@
         }
     }
 
-    final Runnable mAnimationCallback = new Runnable() {
-        @Override
-        public void run() {
-            doAnimation(mChoreographer.getFrameTimeNanos());
-        }
-    };
-
-    final Runnable mRevealAnimationCallback = new Runnable() {
-        @Override
-        public void run() {
-            doRevealAnimation(mChoreographer.getFrameTimeNanos());
-        }
-    };
-
     View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
         public void onFocusChange(View v, boolean hasFocus) {
             // Because 'v' is a ViewGroup, all its children will be (un)selected
@@ -1191,7 +1136,7 @@
         }
     };
 
-    private void makeExpandedVisible(boolean revealAfterDraw) {
+    void makeExpandedVisible(boolean revealAfterDraw) {
         if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
         if (mExpandedVisible) {
             return;
@@ -1218,38 +1163,20 @@
         // Updating the window layout will force an expensive traversal/redraw.
         // Kick off the reveal animation after this is complete to avoid animation latency.
         if (revealAfterDraw) {
-            mHandler.post(mStartRevealAnimation);
+//            mHandler.post(mStartRevealAnimation);
         }
 
         visibilityChanged(true);
     }
 
-    public void animateExpand() {
-        if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
-        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
-            return ;
-        }
-        if (mExpanded) {
-            return;
-        }
-
-        prepareTracking(0, true);
-        mHandler.post(mPerformSelfExpandFling);
-    }
-
     public void animateCollapse() {
         animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
     }
 
     public void animateCollapse(int flags) {
-        animateCollapse(flags, 1.0f);
-    }
-
-    public void animateCollapse(int flags, float velocityMultiplier) {
         if (SPEW) {
-            Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
+            Slog.d(TAG, "animateCollapse(): "
                     + " mExpandedVisible=" + mExpandedVisible
-                    + " mExpanded=" + mExpanded
                     + " mAnimating=" + mAnimating
                     + " mAnimatingReveal=" + mAnimatingReveal
                     + " mAnimY=" + mAnimY
@@ -1267,41 +1194,23 @@
             mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
         }
 
-        if (!mExpandedVisible) {
-            return;
-        }
-
-        int y;
-        if (mAnimating || mAnimatingReveal) {
-            y = (int)mAnimY;
-        } else {
-            y = getExpandedViewMaxHeight()-1;
-        }
-        // Let the fling think that we're open so it goes in the right direction
-        // and doesn't try to re-open the windowshade.
-        mExpanded = true;
-        prepareTracking(y, false);
-        performFling(y, -mSelfCollapseVelocityPx*velocityMultiplier, true);
+        mStatusBarView.collapseAllPanels(true);
     }
 
-    void performExpand() {
-        if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
+    @Override
+    public void animateExpand() {
+        if (SPEW) Slog.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
         if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
             return ;
         }
-        if (mExpanded) {
-            return;
-        }
 
-        mExpanded = true;
-        makeExpandedVisible(false);
-        updateExpandedViewPos(EXPANDED_FULL_OPEN);
+        mNotificationPanel.expand();
 
         if (false) postStartTracing();
     }
 
-    void performCollapse() {
-        if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
+    void makeExpandedInvisible() {
+        if (SPEW) Slog.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
                 + " mExpandedVisible=" + mExpandedVisible);
 
         if (!mExpandedVisible) {
@@ -1309,7 +1218,7 @@
         }
 
         // Ensure the panel is fully collapsed (just in case; bug 6765842)
-        updateExpandedViewPos(0);
+        mStatusBarView.collapseAllPanels(/*animate=*/ false);
 
         mExpandedVisible = false;
         mPile.setLayoutTransitionsEnabled(false);
@@ -1329,11 +1238,6 @@
             setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
         }
 
-        if (!mExpanded) {
-            return;
-        }
-        mExpanded = false;
-
         // Close any "App info" popups that might have snuck on-screen
         dismissPopups();
 
@@ -1343,67 +1247,6 @@
         }
     }
 
-    void resetLastAnimTime() {
-        mAnimLastTimeNanos = System.nanoTime();
-        if (SPEW) {
-            Throwable t = new Throwable();
-            t.fillInStackTrace();
-            Slog.d(TAG, "resetting last anim time=" + mAnimLastTimeNanos, t);
-        }
-    }
-
-    void doAnimation(long frameTimeNanos) {
-        if (mAnimating) {
-            if (SPEW) Slog.d(TAG, "doAnimation dt=" + (frameTimeNanos - mAnimLastTimeNanos));
-            if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
-            incrementAnim(frameTimeNanos);
-            if (SPEW) {
-                Slog.d(TAG, "doAnimation after  mAnimY=" + mAnimY);
-                Slog.d(TAG, "doAnimation expandedViewMax=" + getExpandedViewMaxHeight());
-            }
-
-            if (mAnimY >= getExpandedViewMaxHeight()-1 && !mClosing) {
-                if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
-                mAnimating = false;
-                updateExpandedViewPos(EXPANDED_FULL_OPEN);
-                performExpand();
-                return;
-            }
-
-            if (mAnimY == 0 && mAnimAccel == 0 && mClosing) {
-                if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
-                mAnimating = false;
-                performCollapse();
-                return;
-            }
-
-            if (mAnimY < getStatusBarHeight() && mClosing) {
-                // Draw one more frame with the bar positioned at the top of the screen
-                // before ending the animation so that the user sees the bar in
-                // its final position.  The call to performCollapse() causes a window
-                // relayout which takes time and might cause the animation to skip
-                // on the very last frame before the bar disappears if we did it now.
-                mAnimY = 0;
-                mAnimAccel = 0;
-                mAnimVel = 0;
-            }
-
-            updateExpandedViewPos((int)mAnimY);
-            mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
-                    mAnimationCallback, null);
-        }
-    }
-
-    void stopTracking() {
-        if (!mTracking)
-            return;
-        mTracking = false;
-        setPileLayers(View.LAYER_TYPE_NONE);
-        mVelocityTracker.recycle();
-        mVelocityTracker = null;
-        mCloseView.setPressed(false);
-    }
-
     /**
      * Enables or disables layers on the children of the notifications pile.
      * 
@@ -1451,148 +1294,6 @@
         }
     }
 
-    void incrementAnim(long frameTimeNanos) {
-        final long deltaNanos = Math.max(frameTimeNanos - mAnimLastTimeNanos, 0);
-        final float t = deltaNanos * 0.000000001f;                  // ns -> s
-        final float y = mAnimY;
-        final float v = mAnimVel;                                   // px/s
-        final float a = mAnimAccel;                                 // px/s/s
-        mAnimY = y + (v*t) + (0.5f*a*t*t);                          // px
-        mAnimVel = v + (a*t);                                       // px/s
-        mAnimLastTimeNanos = frameTimeNanos;                        // ns
-        //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
-        //        + " mAnimAccel=" + mAnimAccel);
-    }
-
-    void doRevealAnimation(long frameTimeNanos) {
-        if (SPEW) {
-            Slog.d(TAG, "doRevealAnimation: dt=" + (frameTimeNanos - mAnimLastTimeNanos));
-        }
-        final int h = mNotificationPanelMinHeight;
-        if (mAnimatingReveal && mAnimating && mAnimY < h) {
-            incrementAnim(frameTimeNanos);
-            if (mAnimY >= h) {
-                mAnimY = h;
-                updateExpandedViewPos((int)mAnimY);
-            } else {
-                updateExpandedViewPos((int)mAnimY);
-                mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
-                        mRevealAnimationCallback, null);
-            }
-        }
-    }
-
-    void prepareTracking(int y, boolean opening) {
-        if (CHATTY) {
-            Slog.d(TAG, "panel: beginning to track the user's touch, y=" + y + " opening=" + opening);
-        }
-
-        mCloseView.setPressed(true);
-
-        mTracking = true;
-        setPileLayers(View.LAYER_TYPE_HARDWARE);
-        mVelocityTracker = VelocityTracker.obtain();
-        if (opening) {
-            makeExpandedVisible(true);
-        } else {
-            // it's open, close it?
-            if (mAnimating) {
-                mAnimating = false;
-                mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
-                        mAnimationCallback, null);
-            }
-            updateExpandedViewPos(y + mViewDelta);
-        }
-    }
-
-    void performFling(int y, float vel, boolean always) {
-        if (CHATTY) {
-            Slog.d(TAG, "panel: will fling, y=" + y + " vel=" + vel + " mExpanded=" + mExpanded);
-        }
-
-        mAnimatingReveal = false;
-
-        mAnimY = y;
-        mAnimVel = vel;
-
-        //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
-
-        if (mExpanded) {
-            if (!always && (
-                    vel > mFlingCollapseMinVelocityPx
-                    || (y > (getExpandedViewMaxHeight()*(1f-mCollapseMinDisplayFraction)) &&
-                        vel > -mFlingExpandMinVelocityPx))) {
-                // We are expanded, but they didn't move sufficiently to cause
-                // us to retract.  Animate back to the expanded position.
-                mAnimAccel = mExpandAccelPx;
-                if (vel < 0) {
-                    mAnimVel = 0;
-                }
-            }
-            else {
-                // We are expanded and are now going to animate away.
-                mAnimAccel = -mCollapseAccelPx;
-                if (vel > 0) {
-                    mAnimVel = 0;
-                }
-            }
-        } else {
-            if (always || (
-                    vel > mFlingExpandMinVelocityPx
-                    || (y > (getExpandedViewMaxHeight()*(1f-mExpandMinDisplayFraction)) &&
-                        vel > -mFlingCollapseMinVelocityPx))) {
-                // We are collapsed, and they moved enough to allow us to
-                // expand.  Animate in the notifications.
-                mAnimAccel = mExpandAccelPx;
-                if (vel < 0) {
-                    mAnimVel = 0;
-                }
-            }
-            else {
-                // We are collapsed, but they didn't move sufficiently to cause
-                // us to retract.  Animate back to the collapsed position.
-                mAnimAccel = -mCollapseAccelPx;
-                if (vel > 0) {
-                    mAnimVel = 0;
-                }
-            }
-        }
-        //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
-        //        + " mAnimAccel=" + mAnimAccel);
-
-        resetLastAnimTime();
-        mAnimating = true;
-        mClosing = mAnimAccel < 0;
-
-        mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
-                mAnimationCallback, null);
-        mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
-                mRevealAnimationCallback, null);
-        mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
-                mAnimationCallback, null);
-        stopTracking();
-    }
-
-    boolean handleUniverseEvent(MotionEvent event) {
-        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
-            return false;
-        }
-        if (mExpanded) {
-            return false;
-        }
-        if (mUniverseBackground.consumeEvent(event)) {
-            if (mTracking) {
-                // fling back to the top, starting from the last tracked position.
-                mFlingY = mTrackingPosition;
-                mViewDelta = 0;
-                mFlingVelocity = -1;
-                mHandler.post(mPerformFling);
-            }
-            return true;
-        }
-        return false;
-    }
-
     boolean interceptTouchEvent(MotionEvent event) {
         if (SPEW) {
             Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
@@ -1608,108 +1309,11 @@
 
         mGestureRec.add(event);
 
-        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
-            return false;
-        }
-
-        final int y = (int)event.getRawY();
-        final int action = event.getAction();
-        final int statusBarSize = getStatusBarHeight();
-        final int hitSize = statusBarSize*2;
-        if (action == MotionEvent.ACTION_DOWN) {
-            if (!areLightsOn()) {
-                setLightsOn(true);
-            }
-
-            if (!mExpanded) {
-                mViewDelta = statusBarSize - y;
-            } else {
-                mCloseView.getLocationOnScreen(mAbsPos);
-                mViewDelta = mAbsPos[1]
-                           + getCloseViewHeight() // XXX: not closeViewHeight, but paddingBottom from the 9patch
-                           + mNotificationPanelBackgroundPadding.top
-                           + mNotificationPanelBackgroundPadding.bottom
-                           - y;
-            }
-            if ((!mExpanded && y < hitSize) ||
-                    // @@ add taps outside the panel if it's not full-screen
-                    (mExpanded && y > (getExpandedViewMaxHeight()-hitSize))) {
-                // We drop events at the edge of the screen to make the windowshade come
-                // down by accident less, especially when pushing open a device with a keyboard
-                // that rotates (like g1 and droid)
-                int x = (int)event.getRawX();
-                final int edgeBorder = mEdgeBorder;
-                if (x >= edgeBorder && x < mDisplayMetrics.widthPixels - edgeBorder) {
-                    prepareTracking(y, !mExpanded);// opening if we're not already fully visible
-                    trackMovement(event);
-                    mGestureRec.tag("tracking", mExpanded ? "expanded" : "collapsed");
-                }
-            }
-        } else if (mTracking) {
-            trackMovement(event);
-            if (action == MotionEvent.ACTION_MOVE) {
-                if (mAnimatingReveal && (y + mViewDelta) < mNotificationPanelMinHeight) {
-                    // nothing
-                } else  {
-                    mAnimatingReveal = false;
-                    updateExpandedViewPos(y + mViewDelta);
-                }
-            } else if (action == MotionEvent.ACTION_UP
-                    || action == MotionEvent.ACTION_CANCEL) {
-                mVelocityTracker.computeCurrentVelocity(1000);
-
-                float yVel = mVelocityTracker.getYVelocity();
-                boolean negative = yVel < 0;
-
-                float xVel = mVelocityTracker.getXVelocity();
-                if (xVel < 0) {
-                    xVel = -xVel;
-                }
-                if (xVel > mFlingGestureMaxXVelocityPx) {
-                    xVel = mFlingGestureMaxXVelocityPx; // limit how much we care about the x axis
-                }
-
-                float vel = (float)Math.hypot(yVel, xVel);
-                if (vel > mFlingGestureMaxOutputVelocityPx) {
-                    vel = mFlingGestureMaxOutputVelocityPx;
-                }
-                if (negative) {
-                    vel = -vel;
-                }
-
-                if (CHATTY) {
-                    Slog.d(TAG, String.format("gesture: vraw=(%f,%f) vnorm=(%f,%f) vlinear=%f",
-                        mVelocityTracker.getXVelocity(),
-                        mVelocityTracker.getYVelocity(),
-                        xVel, yVel,
-                        vel));
-                }
-
-                if (mTrackingPosition == mNotificationPanelMinHeight) {
-                    // start the fling from the tracking position, ignore y and view delta
-                    mFlingY = mTrackingPosition;
-                    mViewDelta = 0;
-                } else {
-                    mFlingY = y;
-                }
-                mFlingVelocity = vel;
-                mGestureRec.tag("fling " + ((mFlingVelocity > 0) ? "open" : "closed"),
-                                "v=" + mFlingVelocity);
-                mHandler.post(mPerformFling);
-            }
-
-        }
         return false;
     }
 
-    private void trackMovement(MotionEvent event) {
-        // Add movement to velocity tracker using raw screen X and Y coordinates instead
-        // of window coordinates because the window frame may be moving at the same time.
-        float deltaX = event.getRawX() - event.getX();
-        float deltaY = event.getRawY() - event.getY();
-        event.offsetLocation(deltaX, deltaY);
-        mVelocityTracker.addMovement(event);
-        event.offsetLocation(-deltaX, -deltaY);
+    public GestureRecorder getGestureRecorder() {
+        return mGestureRec;
     }
 
     @Override // CommandQueue
@@ -1913,8 +1517,7 @@
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         synchronized (mQueueLock) {
             pw.println("Current Status Bar state:");
-            pw.println("  mExpanded=" + mExpanded
-                    + ", mExpandedVisible=" + mExpandedVisible
+            pw.println("  mExpandedVisible=" + mExpandedVisible
                     + ", mTrackingPosition=" + mTrackingPosition);
             pw.println("  mTicking=" + mTicking);
             pw.println("  mTracking=" + mTracking);
@@ -1930,7 +1533,6 @@
                     + " mViewDelta=" + mViewDelta);
             pw.println("  mDisplayMetrics=" + mDisplayMetrics);
             pw.println("  mPile: " + viewInfo(mPile));
-            pw.println("  mCloseView: " + viewInfo(mCloseView));
             pw.println("  mTickerView: " + viewInfo(mTickerView));
             pw.println("  mScrollView: " + viewInfo(mScrollView)
                     + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
@@ -2039,83 +1641,17 @@
     }
 
     @Override
-    protected void updateExpandedViewPos(int expandedPosition) {
-        if (SPEW) {
-            Slog.d(TAG, "updateExpandedViewPos: expandedPosition=" + expandedPosition
-                    //+ " mTrackingParams.y=" + ((mTrackingParams == null) ? "?" : mTrackingParams.y)
-                    + " mTracking=" + mTracking
-                    + " mTrackingPosition=" + mTrackingPosition
-                    + " mExpandedVisible=" + mExpandedVisible
-                    + " mAnimating=" + mAnimating
-                    + " mAnimatingReveal=" + mAnimatingReveal
-                    + " mClosing=" + mClosing
-                    + " gravity=" + mNotificationPanelGravity);
-        }
-        int panelh = 0;
-        final int disph = getExpandedViewMaxHeight();
-
-        // If the expanded view is not visible, make sure they're still off screen.
-        // Maybe the view was resized.
-        if (!mExpandedVisible) {
-            if (SPEW) Slog.d(TAG, "updateExpandedViewPos: view not visible, bailing");
-            updateExpandedInvisiblePosition();
-            return;
-        }
-
-        // tracking view...
-        if (expandedPosition == EXPANDED_FULL_OPEN) {
-            panelh = disph;
-        }
-        else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
-            panelh = mTrackingPosition;
-        }
-        else {
-            if (expandedPosition <= disph) {
-                panelh = expandedPosition;
-            } else {
-                panelh = disph;
-            }
-        }
-
-        // catch orientation changes and other peculiar cases
-        if (panelh > 0 &&
-                ((panelh > disph) ||
-                 (panelh < disph && !mTracking && !mAnimating))) {
-            if (SPEW) Slog.d(TAG, "updateExpandedViewPos: orientation change?");
-            panelh = disph;
-        } else if (panelh < 0) {
-            panelh = 0;
-        }
-
-        if (SPEW) Slog.d(TAG, "updateExpandedViewPos: adjusting size to panelh=" + panelh);
-
-        if (panelh == mTrackingPosition) {
-            if (SPEW) Slog.d(TAG, "updateExpandedViewPos: panelh == mTrackingPosition, bailing");
-            return;
-        }
-
-        mTrackingPosition = panelh;
-
+    public void updateExpandedViewPos(int thingy) {
+        // TODO
+        if (DEBUG) Slog.v(TAG, "updateExpandedViewPos");
         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams();
-        lp.height = panelh;
         lp.gravity = mNotificationPanelGravity;
-        lp.leftMargin = mNotificationPanelMarginLeftPx;
-        if (SPEW) {
-            Slog.v(TAG, "updated cropView height=" + panelh + " grav=" + lp.gravity);
-        }
+        lp.leftMargin = mNotificationPanelMarginPx;
         mNotificationPanel.setLayoutParams(lp);
-
-        final int barh = getCloseViewHeight() + getStatusBarHeight();
-        final float frac = saturate((float)(panelh - barh) / (disph - barh));
-
-        if (DIM_BEHIND_EXPANDED_PANEL && ActivityManager.isHighEndGfx(mDisplay)) {
-            // woo, special effects
-            final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2.2f))));
-            final int color = ((int)(0xB0 * k)) << 24;
-            mStatusBarWindow.setBackgroundColor(color);
-        }
-        
-        updateCarrierLabelVisibility(false);
+        lp = (FrameLayout.LayoutParams) mSettingsPanel.getLayoutParams();
+        lp.gravity = mSettingsPanelGravity;
+        lp.rightMargin = mNotificationPanelMarginPx;
+        mSettingsPanel.setLayoutParams(lp);
     }
 
     // called by makeStatusbar and also by PhoneStatusBarView
@@ -2196,6 +1732,9 @@
                         mPostCollapseCleanup = new Runnable() {
                             @Override
                             public void run() {
+                                if (DEBUG) {
+                                    Slog.v(TAG, "running post-collapse cleanup");
+                                }
                                 try {
                                     mPile.setViewRemoval(true);
                                     mBarService.onClearAllNotifications();
@@ -2244,13 +1783,14 @@
             } catch (RemoteException e) {
             }
             v.getContext().startActivityAsUser(new Intent(Settings.ACTION_SETTINGS)
-                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), UserId.USER_CURRENT);
+                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), UserHandle.USER_CURRENT);
             animateCollapse();
         }
     };
 
     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         public void onReceive(Context context, Intent intent) {
+            Slog.v(TAG, "onReceive: " + intent);
             String action = intent.getAction();
             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
                 int flags = CommandQueue.FLAG_EXCLUDE_NONE;
@@ -2264,7 +1804,7 @@
             }
             else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                 // no waiting!
-                performCollapse();
+                makeExpandedInvisible();
             }
             else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
                 updateResources();
@@ -2274,6 +1814,13 @@
         }
     };
 
+    @Override
+    public void userSwitched(int newUserId) {
+        if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
+        animateCollapse();
+        updateNotificationIcons();
+    }
+    
     private void setIntruderAlertVisibility(boolean vis) {
         if (!ENABLE_INTRUDERS) return;
         if (DEBUG) {
@@ -2349,11 +1896,15 @@
 
         mNotificationPanelMarginBottomPx
             = (int) res.getDimension(R.dimen.notification_panel_margin_bottom);
-        mNotificationPanelMarginLeftPx
+        mNotificationPanelMarginPx
             = (int) res.getDimension(R.dimen.notification_panel_margin_left);
         mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
         if (mNotificationPanelGravity <= 0) {
-            mNotificationPanelGravity = Gravity.CENTER_VERTICAL | Gravity.TOP;
+            mNotificationPanelGravity = Gravity.LEFT | Gravity.TOP;
+        }
+        mSettingsPanelGravity = res.getInteger(R.integer.settings_panel_layout_gravity);
+        if (mSettingsPanelGravity <= 0) {
+            mSettingsPanelGravity = Gravity.RIGHT | Gravity.TOP;
         }
         getNinePatchPadding(res.getDrawable(R.drawable.notification_panel_bg), mNotificationPanelBackgroundPadding);
         final int notificationPanelDecorationHeight =
@@ -2416,7 +1967,7 @@
 
     @Override
     protected boolean shouldDisableNavbarGestures() {
-        return mExpanded || (mDisabled & StatusBarManager.DISABLE_HOME) != 0;
+        return mExpandedVisible || (mDisabled & StatusBarManager.DISABLE_HOME) != 0;
     }
 
     private static class FastColorDrawable extends Drawable {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index fd4cff8..2a96d6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -16,9 +16,13 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.app.ActivityManager;
+import android.app.StatusBarManager;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Rect;
 import android.os.SystemClock;
 import android.util.AttributeSet;
@@ -35,133 +39,40 @@
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.policy.FixedSizeDrawable;
 
-public class PhoneStatusBarView extends FrameLayout {
+public class PhoneStatusBarView extends PanelBar {
     private static final String TAG = "PhoneStatusBarView";
-
-    static final int DIM_ANIM_TIME = 400;
-    
-    PhoneStatusBar mService;
-    boolean mTracking;
-    int mStartX, mStartY;
-    ViewGroup mNotificationIcons;
-    ViewGroup mStatusIcons;
-    
-    boolean mNightMode = false;
-    int mStartAlpha = 0, mEndAlpha = 0;
-    long mEndTime = 0;
-
-    Rect mButtonBounds = new Rect();
-    boolean mCapturingEvents = true;
+    PhoneStatusBar mBar;
+    int mScrimColor;
+    PanelView mFadingPanel = null;
+    PanelView mNotificationPanel, mSettingsPanel;
 
     public PhoneStatusBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mNotificationIcons = (ViewGroup)findViewById(R.id.notificationIcons);
-        mStatusIcons = (ViewGroup)findViewById(R.id.statusIcons);
+    public void setBar(PhoneStatusBar bar) {
+        mBar = bar;
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        //mService.onBarViewAttached();
-    }
-    
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        mService.updateDisplaySize();
-        boolean nightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
-                == Configuration.UI_MODE_NIGHT_YES;
-        if (mNightMode != nightMode) {
-            mNightMode = nightMode;
-            mStartAlpha = getCurAlpha();
-            mEndAlpha = mNightMode ? 0x80 : 0x00;
-            mEndTime = SystemClock.uptimeMillis() + DIM_ANIM_TIME;
-            invalidate();
-        }
-    }
-
-    int getCurAlpha() {
-        long time = SystemClock.uptimeMillis();
-        if (time > mEndTime) {
-            return mEndAlpha;
-        }
-        return mEndAlpha
-                - (int)(((mEndAlpha-mStartAlpha) * (mEndTime-time) / DIM_ANIM_TIME));
-    }
-    
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-        mService.updateExpandedViewPos(BaseStatusBar.EXPANDED_LEAVE_ALONE);
+    public void onAttachedToWindow() {
+        Resources res = getContext().getResources();
+        mScrimColor = res.getColor(R.color.notification_panel_scrim_color);
     }
 
     @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
+    public void addPanel(PanelView pv) {
+        super.addPanel(pv);
+        if (pv.getId() == R.id.notification_panel) {
+            mNotificationPanel = pv;
+        } else if (pv.getId() == R.id.settings_panel){
+            mSettingsPanel = pv;
+        }
     }
 
     @Override
-    protected void dispatchDraw(Canvas canvas) {
-        super.dispatchDraw(canvas);
-        int alpha = getCurAlpha();
-        if (alpha != 0) {
-            canvas.drawARGB(alpha, 0, 0, 0);
-        }
-        if (alpha != mEndAlpha) {
-            invalidate();
-        }
-    }
-
-    /**
-     * Gets the left position of v in this view.  Throws if v is not
-     * a child of this.
-     */
-    private int getViewOffset(View v) {
-        int offset = 0;
-        while (v != this) {
-            offset += v.getLeft();
-            ViewParent p = v.getParent();
-            if (v instanceof View) {
-                v = (View)p;
-            } else {
-                throw new RuntimeException(v + " is not a child of " + this);
-            }
-        }
-        return offset;
-    }
-
-    /**
-     * Ensure that, if there is no target under us to receive the touch,
-     * that we process it ourself.  This makes sure that onInterceptTouchEvent()
-     * is always called for the entire gesture.
-     */
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        if (!mCapturingEvents) {
-            return false;
-        }
-        if (event.getAction() != MotionEvent.ACTION_DOWN) {
-            mService.interceptTouchEvent(event);
-        }
-        return true;
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent event) {
-        if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            if (mButtonBounds.contains((int)event.getX(), (int)event.getY())) {
-                mCapturingEvents = false;
-                return false;
-            }
-        }
-        mCapturingEvents = true;
-        return mService.interceptTouchEvent(event)
-                ? true : super.onInterceptTouchEvent(event);
+    public boolean panelsEnabled() {
+        return ((mBar.mDisabled & StatusBarManager.DISABLE_EXPAND) == 0);
     }
 
     @Override
@@ -178,4 +89,67 @@
         }
         return false;
     }
+
+    @Override
+    public PanelView selectPanelForTouchX(float x) {
+        // We split the status bar into thirds: the left 2/3 are for notifications, and the 
+        // right 1/3 for quick settings. If you pull the status bar down a second time you'll
+        // toggle panels no matter where you pull it down.
+        final float w = (float) getMeasuredWidth();
+        final float f = x / w;
+        if (f > 0.67f && mSettingsPanel.getExpandedFraction() != 1.0f
+                || mNotificationPanel.getExpandedFraction() == 1.0f) {
+            return mSettingsPanel;
+        }
+        return mNotificationPanel;
+    }
+
+    @Override
+    public void onPanelPeeked() {
+        super.onPanelPeeked();
+        mBar.makeExpandedVisible(true);
+        if (mFadingPanel == null) {
+            mFadingPanel = mTouchingPanel;
+        }
+    }
+
+    @Override
+    public void onAllPanelsCollapsed() {
+        super.onAllPanelsCollapsed();
+        mBar.makeExpandedInvisible();
+        mFadingPanel = null;
+    }
+
+    @Override
+    public void onPanelFullyOpened(PanelView openPanel) {
+        mFadingPanel = openPanel;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        return mBar.interceptTouchEvent(event) || super.onTouchEvent(event);
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent event) {
+        return mBar.interceptTouchEvent(event) || super.onInterceptTouchEvent(event);
+    }
+
+    @Override
+    public void panelExpansionChanged(PanelView pv, float frac) {
+        super.panelExpansionChanged(pv, frac);
+
+        if (mFadingPanel == pv 
+                && mScrimColor != 0 && ActivityManager.isHighEndGfx(mBar.mDisplay)) {
+            // woo, special effects
+            final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2.2f))));
+            // attenuate background color alpha by k
+            final int color = (int) ((float)(mScrimColor >>> 24) * k) << 24 | (mScrimColor & 0xFFFFFF);
+            mBar.mStatusBarWindow.setBackgroundColor(color);
+        }
+
+        mBar.updateCarrierLabelVisibility(false);
+    }
+
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
new file mode 100644
index 0000000..fb1528f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
@@ -0,0 +1,34 @@
+/*
+ * 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.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+public class SettingsPanelView extends PanelView {
+    public SettingsPanelView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void fling(float vel, boolean always) {
+        ((PhoneStatusBarView) mBar).mBar.getGestureRecorder().tag(
+            "fling " + ((vel > 0) ? "open" : "closed"),
+            "settings,v=" + vel);
+        super.fling(vel, always);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index f53ed0c..2d4c9ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -37,8 +37,6 @@
     private ExpandHelper mExpandHelper;
     private NotificationRowLayout latestItems;
 
-    private boolean mUniverseHandling = false;
-
     PhoneStatusBar mService;
 
     public StatusBarWindowView(Context context, AttributeSet attrs) {
@@ -73,16 +71,6 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (mService.handleUniverseEvent(ev)) {
-            mUniverseHandling = true;
-            MotionEvent cancellation = MotionEvent.obtain(ev);
-            cancellation.setAction(MotionEvent.ACTION_CANCEL);
-            mExpandHelper.onInterceptTouchEvent(cancellation);
-            latestItems.onInterceptTouchEvent(cancellation);
-            cancellation.recycle();
-            return true;
-        }
-
         boolean intercept = mExpandHelper.onInterceptTouchEvent(ev) ||
                 super.onInterceptTouchEvent(ev);
         if (intercept) {
@@ -96,12 +84,6 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        if (mUniverseHandling) {
-            if (!mService.handleUniverseEvent(ev)) {
-                mUniverseHandling = false;
-            }
-            return true;
-        }
         boolean handled = mExpandHelper.onTouchEvent(ev) ||
                 super.onTouchEvent(ev);
         return handled;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessController.java
index 3ba36af..457e9dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessController.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.os.AsyncTask;
 import android.os.IPowerManager;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Settings;
@@ -31,8 +32,8 @@
 public class BrightnessController implements ToggleSlider.Listener {
     private static final String TAG = "StatusBar.BrightnessController";
 
-    private static final int MINIMUM_BACKLIGHT = android.os.PowerManager.BRIGHTNESS_DIM;
-    private static final int MAXIMUM_BACKLIGHT = android.os.PowerManager.BRIGHTNESS_ON;
+    private final int mMinimumBacklight;
+    private final int mMaximumBacklight;
 
     private Context mContext;
     private ToggleSlider mControl;
@@ -42,6 +43,10 @@
         mContext = context;
         mControl = control;
 
+        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+        mMinimumBacklight = pm.getMinimumScreenBrightnessSetting();
+        mMaximumBacklight = pm.getMaximumScreenBrightnessSetting();
+
         boolean automaticAvailable = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_automatic_brightness_available);
         mPower = IPowerManager.Stub.asInterface(ServiceManager.getService("power"));
@@ -65,11 +70,11 @@
             value = Settings.System.getInt(mContext.getContentResolver(), 
                     Settings.System.SCREEN_BRIGHTNESS);
         } catch (SettingNotFoundException ex) {
-            value = MAXIMUM_BACKLIGHT;
+            value = mMaximumBacklight;
         }
 
-        control.setMax(MAXIMUM_BACKLIGHT - MINIMUM_BACKLIGHT);
-        control.setValue(value - MINIMUM_BACKLIGHT);
+        control.setMax(mMaximumBacklight - mMinimumBacklight);
+        control.setValue(value - mMinimumBacklight);
 
         control.setOnChangedListener(this);
     }
@@ -78,7 +83,7 @@
         setMode(automatic ? Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC
                 : Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
         if (!automatic) {
-            final int val = value + MINIMUM_BACKLIGHT;
+            final int val = value + mMinimumBacklight;
             setBrightness(val);
             if (!tracking) {
                 AsyncTask.execute(new Runnable() {
@@ -98,7 +103,7 @@
     
     private void setBrightness(int brightness) {
         try {
-            mPower.setBacklightBrightness(brightness);
+            mPower.setTemporaryScreenBrightnessSettingOverride(brightness);
         } catch (RemoteException ex) {
         }        
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 69872df..ffc18c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -34,6 +34,7 @@
 import android.text.style.RelativeSizeSpan;
 import android.text.style.StyleSpan;
 import android.util.AttributeSet;
+import android.util.Slog;
 import android.view.View;
 import android.widget.TextView;
 
@@ -173,7 +174,6 @@
                         + "a" + MAGIC2 + format.substring(b + 1);
                 }
             }
-
             mClockFormat = sdf = new SimpleDateFormat(format);
             mClockFormatString = format;
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
index 9fee49b..89eed1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
@@ -78,6 +78,7 @@
         super(context, attrs, defStyle);
 
         mRealLayoutTransition = new LayoutTransition();
+        mRealLayoutTransition.setAnimateParentHierarchy(true);
         setLayoutTransitionsEnabled(true);
         
         setOrientation(LinearLayout.VERTICAL);
@@ -166,6 +167,7 @@
     }
 
     public void onChildDismissed(View v) {
+        if (DEBUG) Slog.v(TAG, "onChildDismissed: " + v + " mRemoveViews=" + mRemoveViews);
         final View veto = v.findViewById(R.id.veto);
         if (veto != null && veto.getVisibility() != View.GONE && mRemoveViews) {
             veto.performClick();
@@ -229,6 +231,7 @@
      * get removed properly.
      */
     public void setViewRemoval(boolean removeViews) {
+        if (DEBUG) Slog.v(TAG, "setViewRemoval: " + removeViews);
         mRemoveViews = removeViews;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
index da161a9..ffe69e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
@@ -19,7 +19,7 @@
 import android.app.StatusBarManager;
 import android.content.Context;
 import android.content.Intent;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.Slog;
@@ -119,7 +119,7 @@
     // ----------------------------
     private void onClickSettings() {
         getContext().startActivityAsUser(new Intent(Settings.ACTION_SETTINGS)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), UserId.USER_CURRENT);
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), UserHandle.USER_CURRENT);
         getStatusBarManager().collapse();
     }
 }
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
index a939222..279314d 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
@@ -178,10 +178,10 @@
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_DPM_STATE_CHANGED));
             } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED,
-                        intent.getIntExtra(Intent.EXTRA_USERID, 0), 0));
+                        intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_REMOVED,
-                        intent.getIntExtra(Intent.EXTRA_USERID, 0), 0));
+                        intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
             }
         }
     };
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index 1fb63db..66ccdac 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -33,7 +33,7 @@
 import android.media.AudioManager;
 import android.media.SoundPool;
 import android.os.Handler;
-import android.os.LocalPowerManager;
+import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -156,10 +156,6 @@
     private boolean mSuppressNextLockSound = true;
 
 
-    /** Low level access to the power manager for enableUserActivity.  Having this
-     * requires that we run in the system process.  */
-    LocalPowerManager mRealPowerManager;
-
     /** High level access to the power manager for WakeLocks */
     private PowerManager mPM;
 
@@ -228,7 +224,7 @@
 
     private KeyguardUpdateMonitor mUpdateMonitor;
 
-    private boolean mScreenOn = false;
+    private boolean mScreenOn;
 
     // last known state of the cellular connection
     private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE;
@@ -357,11 +353,9 @@
 
     };
 
-    public KeyguardViewMediator(Context context, PhoneWindowManager callback,
-            LocalPowerManager powerManager) {
+    public KeyguardViewMediator(Context context, PhoneWindowManager callback) {
         mContext = context;
         mCallback = callback;
-        mRealPowerManager = powerManager;
         mPM = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mWakeLock = mPM.newWakeLock(
                 PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "keyguard");
@@ -393,6 +387,8 @@
         final ContentResolver cr = mContext.getContentResolver();
         mShowLockIcon = (Settings.System.getInt(cr, "show_status_bar_lock", 0) == 1);
 
+        mScreenOn = mPM.isScreenOn();
+
         mLockSounds = new SoundPool(1, AudioManager.STREAM_SYSTEM, 0);
         String soundPath = Settings.System.getString(cr, Settings.System.LOCK_SOUND);
         if (soundPath != null) {
@@ -974,7 +970,7 @@
      * interacts with the keyguard ui should be posted to this handler, rather
      * than called directly.
      */
-    private Handler mHandler = new Handler() {
+    private Handler mHandler = new Handler(Looper.myLooper(), null, true /*async*/) {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -1031,7 +1027,7 @@
         if (DEBUG) Log.d(TAG, "handleKeyguardDone");
         handleHide();
         if (wakeup) {
-            mPM.userActivity(SystemClock.uptimeMillis(), true);
+            mPM.wakeUp(SystemClock.uptimeMillis());
         }
         mWakeLock.release();
         mContext.sendBroadcast(mUserPresentIntent);
@@ -1164,7 +1160,8 @@
         // disable user activity if we are shown and not hidden
         if (DEBUG) Log.d(TAG, "adjustUserActivityLocked mShowing: " + mShowing + " mHidden: " + mHidden);
         boolean enabled = !mShowing || mHidden;
-        mRealPowerManager.enableUserActivity(enabled);
+        // FIXME: Replace this with a new timeout control mechanism.
+        //mRealPowerManager.enableUserActivity(enabled);
         if (!enabled && mScreenOn) {
             // reinstate our short screen timeout policy
             pokeWakelock();
diff --git a/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java
index 82181d3..5f5c105 100644
--- a/policy/src/com/android/internal/policy/impl/LockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/LockScreen.java
@@ -32,6 +32,7 @@
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.os.UserHandle;
 import android.os.Vibrator;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -275,7 +276,8 @@
 
             // Update the search icon with drawable from the search .apk
             if (!mSearchDisabled) {
-                Intent intent = SearchManager.getAssistIntent(mContext);
+                Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+                        .getAssistIntent(mContext, UserHandle.USER_CURRENT);
                 if (intent != null) {
                     // XXX Hack. We need to substitute the icon here but haven't formalized
                     // the public API. The "_google" metadata will be going away, so
@@ -309,7 +311,9 @@
             final int resId = mGlowPadView.getResourceIdForTarget(target);
             switch (resId) {
                 case com.android.internal.R.drawable.ic_action_assist_generic:
-                    Intent assistIntent = SearchManager.getAssistIntent(mContext);
+                    Intent assistIntent =
+                            ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+                            .getAssistIntent(mContext, UserHandle.USER_CURRENT);
                     if (assistIntent != null) {
                         launchActivity(assistIntent);
                     } else {
@@ -335,6 +339,10 @@
             }
         }
 
+        /**
+         * Launches the said intent for the current foreground user.
+         * @param intent
+         */
         private void launchActivity(Intent intent) {
             intent.setFlags(
                     Intent.FLAG_ACTIVITY_NEW_TASK
@@ -346,7 +354,7 @@
                 Log.w(TAG, "can't dismiss keyguard on launch");
             }
             try {
-                mContext.startActivity(intent);
+                mContext.startActivityAsUser(intent, UserHandle.USER_CURRENT);
             } catch (ActivityNotFoundException e) {
                 Log.w(TAG, "Activity not found for intent + " + intent.getAction());
             }
@@ -522,7 +530,9 @@
         } else if (disabledBySimState) {
             Log.v(TAG, "Camera disabled by Sim State");
         }
-        boolean searchActionAvailable = SearchManager.getAssistIntent(mContext) != null;
+        boolean searchActionAvailable =
+                ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+                .getAssistIntent(mContext, UserHandle.USER_CURRENT) != null;
         mCameraDisabled = disabledByAdmin || disabledBySimState || !cameraTargetPresent;
         mSearchDisabled = disabledBySimState || !searchActionAvailable || !searchTargetPresent;
         mUnlockWidgetMethods.updateResources();
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 8c627a3..40db612 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -45,7 +45,6 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IRemoteCallback;
-import android.os.LocalPowerManager;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
@@ -55,6 +54,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UEventObserver;
+import android.os.UserHandle;
 import android.os.Vibrator;
 import android.provider.Settings;
 
@@ -169,9 +169,6 @@
     static final boolean ENABLE_CAR_DOCK_HOME_CAPTURE = true;
     static final boolean ENABLE_DESK_DOCK_HOME_CAPTURE = false;
 
-    // Should screen savers use their own timeout, or the SCREEN_OFF_TIMEOUT?
-    static final boolean SEPARATE_TIMEOUT_FOR_SCREEN_SAVER = false;
-
     static final int LONG_PRESS_POWER_NOTHING = 0;
     static final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;
     static final int LONG_PRESS_POWER_SHUT_OFF = 2;
@@ -279,7 +276,7 @@
     Context mContext;
     IWindowManager mWindowManager;
     WindowManagerFuncs mWindowManagerFuncs;
-    LocalPowerManager mPowerManager;
+    PowerManager mPowerManager;
     IStatusBarService mStatusBarService;
     final Object mServiceAquireLock = new Object();
     Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
@@ -473,13 +470,6 @@
     int mLockScreenTimeout;
     boolean mLockScreenTimerActive;
 
-    // visual screen saver support
-    boolean mScreenSaverFeatureAvailable;
-    int mScreenSaverTimeout = 0;
-    boolean mScreenSaverEnabledByUser = false;
-    boolean mScreenSaverMayRun = true; // false if a wakelock is held
-    boolean mPluggedIn;
-
     // Behavior of ENDCALL Button.  (See Settings.System.END_BUTTON_BEHAVIOR.)
     int mEndcallBehavior;
 
@@ -571,12 +561,6 @@
                     Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
             resolver.registerContentObserver(Settings.System.getUriFor(
                     "fancy_rotation_anim"), false, this);
-            resolver.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.SCREENSAVER_ENABLED), false, this);
-            if (SEPARATE_TIMEOUT_FOR_SCREEN_SAVER) {
-                resolver.registerContentObserver(Settings.Secure.getUriFor(
-                        "screensaver_timeout"), false, this);
-            } // otherwise SCREEN_OFF_TIMEOUT will do nicely
             updateSettings();
         }
 
@@ -862,16 +846,14 @@
 
     /** {@inheritDoc} */
     public void init(Context context, IWindowManager windowManager,
-            WindowManagerFuncs windowManagerFuncs,
-            LocalPowerManager powerManager) {
+            WindowManagerFuncs windowManagerFuncs) {
         mContext = context;
         mWindowManager = windowManager;
         mWindowManagerFuncs = windowManagerFuncs;
-        mPowerManager = powerManager;
         mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
         if (!mHeadless) {
             // don't create KeyguardViewMediator if headless
-            mKeyguardMediator = new KeyguardViewMediator(context, this, powerManager);
+            mKeyguardMediator = new KeyguardViewMediator(context, this);
         }
         mHandler = new PolicyHandler();
         mOrientationListener = new MyOrientationListener(mContext);
@@ -897,8 +879,8 @@
         mDeskDockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
 
-        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
-        mBroadcastWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+        mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+        mBroadcastWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                 "PhoneWindowManager.mBroadcastWakeLock");
         mEnableShiftMenuBugReports = "1".equals(SystemProperties.get("ro.debuggable"));
         mLidOpenRotation = readRotation(
@@ -931,14 +913,6 @@
                     Intent.EXTRA_DOCK_STATE_UNDOCKED);
         }
 
-        // watch the plug to know whether to trigger the screen saver
-        filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
-        intent = context.registerReceiver(mPowerReceiver, filter);
-        if (intent != null) {
-            mPluggedIn = (0 != intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0));
-        }
-
         mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
         mLongPressVibePattern = getLongIntArray(mContext.getResources(),
                 com.android.internal.R.array.config_longPressVibePattern);
@@ -1120,26 +1094,6 @@
                 mHasSoftInput = hasSoftInput;
                 updateRotation = true;
             }
-
-            // dreams
-            mScreenSaverFeatureAvailable = mContext.getResources().getBoolean(
-                    com.android.internal.R.bool.config_enableDreams);
-            
-            mScreenSaverEnabledByUser = 0 != Settings.Secure.getInt(resolver,
-                    Settings.Secure.SCREENSAVER_ENABLED, 0);
-
-            if (SEPARATE_TIMEOUT_FOR_SCREEN_SAVER) {
-                mScreenSaverTimeout = Settings.Secure.getInt(resolver,
-                        "screensaver_timeout", 0);
-            } else {
-                mScreenSaverTimeout = Settings.System.getInt(resolver,
-                        Settings.System.SCREEN_OFF_TIMEOUT, 0);
-                if (mScreenSaverTimeout > 0) {
-                    // We actually want to activate the screensaver just before the
-                    // power manager's screen timeout
-                    mScreenSaverTimeout -= 5000;
-                }
-            }
         }
         if (updateRotation) {
             updateRotation(true);
@@ -1763,15 +1717,17 @@
                 mHomePressed = false;
                 mHomeLongPressed = false;
                 if (!homeWasLongPressed) {
-                    try {
-                        IStatusBarService statusbar = getStatusBarService();
-                        if (statusbar != null) {
-                            statusbar.cancelPreloadRecentApps();
+                    if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI) {
+                        try {
+                            IStatusBarService statusbar = getStatusBarService();
+                            if (statusbar != null) {
+                                statusbar.cancelPreloadRecentApps();
+                            }
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "RemoteException when showing recent apps", e);
+                            // re-acquire status bar service next time it is needed.
+                            mStatusBarService = null;
                         }
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "RemoteException when showing recent apps", e);
-                        // re-acquire status bar service next time it is needed.
-                        mStatusBarService = null;
                     }
 
                     mHomePressed = false;
@@ -2110,7 +2066,7 @@
             if (searchManager != null) {
                 searchManager.stopSearch();
             }
-            mContext.startActivity(intent);
+            mContext.startActivityAsUser(intent, UserHandle.USER_CURRENT);
         } catch (ActivityNotFoundException e) {
             Slog.w(TAG, "No activity to handle assist long press action.", e);
         }
@@ -2118,13 +2074,14 @@
 
     private void launchAssistAction() {
         sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST);
-        Intent intent = SearchManager.getAssistIntent(mContext);
+        Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+                .getAssistIntent(mContext, UserHandle.USER_CURRENT);
         if (intent != null) {
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                     | Intent.FLAG_ACTIVITY_SINGLE_TOP
                     | Intent.FLAG_ACTIVITY_CLEAR_TOP);
             try {
-                mContext.startActivity(intent);
+                mContext.startActivityAsUser(intent, UserHandle.USER_CURRENT);
             } catch (ActivityNotFoundException e) {
                 Slog.w(TAG, "No activity to handle assist action.", e);
             }
@@ -3016,12 +2973,10 @@
                 mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(
                         KeyEvent.KEYCODE_POWER, mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED);
             } else {
-                mPowerManager.userActivity(SystemClock.uptimeMillis(), false,
-                        PowerManager.USER_ACTIVITY_EVENT_BUTTON);
+                mPowerManager.wakeUp(SystemClock.uptimeMillis());
             }
         } else if (!mLidControlsSleep) {
-            mPowerManager.userActivity(SystemClock.uptimeMillis(), false,
-                    PowerManager.USER_ACTIVITY_EVENT_OTHER);
+            mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
         }
     }
 
@@ -3180,6 +3135,11 @@
     /** {@inheritDoc} */
     @Override
     public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
+        if (!mSystemBooted) {
+            // If we have not yet booted, don't let key events do anything.
+            return 0;
+        }
+
         final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
         final boolean canceled = event.isCanceled();
         final int keyCode = event.getKeyCode();
@@ -3195,14 +3155,17 @@
                                                 mKeyguardMediator.isShowingAndNotHidden() :
                                                 mKeyguardMediator.isShowing()));
 
-        if (!mSystemBooted) {
-            // If we have not yet booted, don't let key events do anything.
-            return 0;
+        if (keyCode == KeyEvent.KEYCODE_POWER) {
+            policyFlags |= WindowManagerPolicy.FLAG_WAKE;
         }
+        final boolean isWakeKey = (policyFlags & (WindowManagerPolicy.FLAG_WAKE
+                | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
 
         if (DEBUG_INPUT) {
             Log.d(TAG, "interceptKeyTq keycode=" + keyCode
-                  + " screenIsOn=" + isScreenOn + " keyguardActive=" + keyguardActive);
+                    + " screenIsOn=" + isScreenOn + " keyguardActive=" + keyguardActive
+                    + " policyFlags=" + Integer.toHexString(policyFlags)
+                    + " isWakeKey=" + isWakeKey);
         }
 
         if (down && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0
@@ -3210,12 +3173,6 @@
             performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
         }
 
-        if (keyCode == KeyEvent.KEYCODE_POWER) {
-            policyFlags |= WindowManagerPolicy.FLAG_WAKE;
-        }
-        final boolean isWakeKey = (policyFlags & (WindowManagerPolicy.FLAG_WAKE
-                | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
-
         // Basic policy based on screen state and keyguard.
         // FIXME: This policy isn't quite correct.  We shouldn't care whether the screen
         //        is on or off, really.  We should care about whether the device is in an
@@ -3239,7 +3196,7 @@
                             mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED);
                 } else {
                     // Otherwise, wake the device ourselves.
-                    result |= ACTION_POKE_USER_ACTIVITY;
+                    result |= ACTION_WAKE_UP;
                 }
             }
         }
@@ -3344,7 +3301,7 @@
                         }
                         if ((mEndcallBehavior
                                 & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
-                            result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP;
+                            result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP;
                         }
                     }
                 }
@@ -3386,7 +3343,7 @@
                     mPowerKeyTriggered = false;
                     cancelPendingScreenshotChordAction();
                     if (interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) {
-                        result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP;
+                        result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP;
                     }
                     mPendingPowerKeyUpCanceled = false;
                 }
@@ -3471,7 +3428,7 @@
                 mKeyguardMediator.onWakeMotionWhenKeyguardShowingTq();
             } else {
                 // Otherwise, wake the device ourselves.
-                result |= ACTION_POKE_USER_ACTIVITY;
+                result |= ACTION_WAKE_UP;
             }
         }
         return result;
@@ -3551,15 +3508,6 @@
         }
     };
 
-    BroadcastReceiver mPowerReceiver = new BroadcastReceiver() {
-        public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
-                mPluggedIn = (0 != intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0));
-                if (localLOGV) Log.v(TAG, "BATTERY_CHANGED: " + intent + " plugged=" + mPluggedIn);
-            }
-        }
-    };
-
     /** {@inheritDoc} */
     public void screenTurnedOff(int why) {
         EventLog.writeEvent(70000, 0);
@@ -4033,62 +3981,6 @@
         }
     }
 
-    private IDreamManager getDreamManager() {
-        if (!mScreenSaverFeatureAvailable) {
-            return null;
-        }
-        
-        IDreamManager sandman = IDreamManager.Stub.asInterface(
-                ServiceManager.checkService("dreams"));
-        if (sandman == null) {
-            Log.w(TAG, "Unable to find IDreamManager");
-        }
-        return sandman;
-    }
-
-    @Override
-    public boolean isScreenSaverEnabled() {
-        return (mScreenSaverFeatureAvailable && mScreenSaverEnabledByUser
-                && mScreenSaverMayRun && mScreenOnEarly && mPluggedIn);
-    }
-
-    @Override
-    public boolean startScreenSaver() {
-        synchronized (mLock) {
-            if (isScreenSaverEnabled()) {
-                IDreamManager dm = getDreamManager();
-                if (dm == null) return false;
-                
-                try {
-                    if (localLOGV) Log.v(TAG, "startScreenSaver: entering dreamland...");
-
-                    dm.dream();
-                    return true;
-                } catch (RemoteException ex) {
-                    // too bad, so sad, oh mom, oh dad
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public void stopScreenSaver() {
-        synchronized (mLock) {
-            IDreamManager dm = getDreamManager();
-            if (dm == null) return;
-            
-            try {
-                if (!dm.isDreaming()) return;
-
-                if (localLOGV) Log.v(TAG, "stopScreenSaver: awakening...");
-                
-                dm.awaken();
-            } catch (RemoteException ex) {
-            }
-        }
-    }
-
     Runnable mScreenLockTimeout = new Runnable() {
         public void run() {
             synchronized (this) {
@@ -4132,8 +4024,6 @@
     }
 
     private void applyLidSwitchState() {
-        mPowerManager.setKeyboardVisibility(isBuiltInKeyboardVisible());
-
         if (mLidState == LID_CLOSED && mLidControlsSleep) {
             mPowerManager.goToSleep(SystemClock.uptimeMillis());
         }
@@ -4315,24 +4205,13 @@
     }
     
     public void screenOnStartedLw() {
-        // The window manager has just grabbed a wake lock. This is our cue to disable the screen
-        // saver.
-        synchronized (mLock) {
-            mScreenSaverMayRun = false;
-        }
     }
 
     public void screenOnStoppedLw() {
         if (mPowerManager.isScreenOn()) {
             if (mKeyguardMediator != null && !mKeyguardMediator.isShowingAndNotHidden()) {
                 long curTime = SystemClock.uptimeMillis();
-                mPowerManager.userActivity(curTime, false, PowerManager.USER_ACTIVITY_EVENT_OTHER);
-            }
-
-            synchronized (mLock) {
-                // even if the keyguard is up, now that all the wakelocks have been released, we
-                // should re-enable the screen saver
-                mScreenSaverMayRun = true;
+                mPowerManager.userActivity(curTime, false);
             }
         }
     }
diff --git a/preloaded-classes b/preloaded-classes
index c29ba15..feddbd6 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -38,10 +38,6 @@
 android.animation.TypeEvaluator
 android.animation.ValueAnimator
 android.animation.ValueAnimator$1
-android.animation.ValueAnimator$2
-android.animation.ValueAnimator$3
-android.animation.ValueAnimator$4
-android.animation.ValueAnimator$5
 android.animation.ValueAnimator$AnimationHandler
 android.app.ActionBar
 android.app.ActionBar$LayoutParams
@@ -279,7 +275,6 @@
 android.content.res.ObbInfo$1
 android.content.res.ObbScanner
 android.content.res.Resources
-android.content.res.Resources$1
 android.content.res.Resources$Theme
 android.content.res.StringBlock
 android.content.res.StringBlock$StyleIDs
@@ -318,13 +313,11 @@
 android.database.Observable
 android.database.sqlite.DatabaseObjectNotClosedException
 android.database.sqlite.SQLiteClosable
-android.database.sqlite.SQLiteCompiledSql
 android.database.sqlite.SQLiteCursor
 android.database.sqlite.SQLiteCursorDriver
 android.database.sqlite.SQLiteDatabase
 android.database.sqlite.SQLiteDatabase$1
 android.database.sqlite.SQLiteDatabase$CustomFunction
-android.database.sqlite.SQLiteDatabase$DatabaseReentrantLock
 android.database.sqlite.SQLiteDebug
 android.database.sqlite.SQLiteDebug$PagerStats
 android.database.sqlite.SQLiteDirectCursorDriver
@@ -482,7 +475,6 @@
 android.media.AudioFormat
 android.media.AudioManager
 android.media.AudioManager$1
-android.media.AudioManager$2
 android.media.AudioManager$FocusEventHandlerDelegate
 android.media.AudioManager$FocusEventHandlerDelegate$1
 android.media.AudioRecord
@@ -495,8 +487,6 @@
 android.media.IAudioService
 android.media.IAudioService$Stub
 android.media.IAudioService$Stub$Proxy
-android.media.IRemoteControlClientDispatcher
-android.media.IRemoteControlClientDispatcher$Stub
 android.media.JetPlayer
 android.media.MediaFile
 android.media.MediaPlayer
@@ -605,7 +595,6 @@
 android.os.Parcelable$Creator
 android.os.PatternMatcher
 android.os.PatternMatcher$1
-android.os.Power
 android.os.PowerManager
 android.os.PowerManager$WakeLock
 android.os.PowerManager$WakeLock$1
@@ -698,7 +687,6 @@
 android.text.TextDirectionHeuristics
 android.text.TextDirectionHeuristics$1
 android.text.TextDirectionHeuristics$AnyStrong
-android.text.TextDirectionHeuristics$CharCount
 android.text.TextDirectionHeuristics$FirstStrong
 android.text.TextDirectionHeuristics$TextDirectionAlgorithm
 android.text.TextDirectionHeuristics$TextDirectionHeuristicImpl
@@ -729,7 +717,6 @@
 android.text.method.TransformationMethod
 android.text.method.TransformationMethod2
 android.text.method.WordIterator
-android.text.method.WordIterator$1
 android.text.style.AlignmentSpan
 android.text.style.CharacterStyle
 android.text.style.LeadingMarginSpan
@@ -824,10 +811,9 @@
 android.view.InputEvent$1
 android.view.InputEventConsistencyVerifier
 android.view.InputEventConsistencyVerifier$KeyState
-android.view.InputHandler
+android.view.InputEventReceiver
 android.view.InputQueue
 android.view.InputQueue$Callback
-android.view.InputQueue$FinishedCallback
 android.view.KeyCharacterMap
 android.view.KeyCharacterMap$FallbackAction
 android.view.KeyEvent
@@ -903,7 +889,6 @@
 android.view.ViewParent
 android.view.ViewRootImpl
 android.view.ViewRootImpl$2
-android.view.ViewRootImpl$3
 android.view.ViewRootImpl$AccessibilityInteractionConnectionManager
 android.view.ViewRootImpl$InputMethodCallback
 android.view.ViewRootImpl$ResizedInfo
@@ -1005,8 +990,11 @@
 android.widget.CursorAdapter
 android.widget.CursorFilter$CursorFilterClient
 android.widget.EdgeEffect
-android.widget.EdgeGlow
 android.widget.EditText
+android.widget.Editor$Blink
+android.widget.Editor$EasyEditSpanController
+android.widget.Editor$InputContentType
+android.widget.Editor$InputMethodState
 android.widget.ExpandableListView
 android.widget.FastScroller
 android.widget.FastScroller$1
@@ -1080,17 +1068,12 @@
 android.widget.TextView
 android.widget.TextView$2
 android.widget.TextView$3
-android.widget.TextView$Blink
 android.widget.TextView$BufferType
 android.widget.TextView$ChangeWatcher
 android.widget.TextView$CharWrapper
 android.widget.TextView$Drawables
-android.widget.TextView$EasyEditSpanController
-android.widget.TextView$InputContentType
-android.widget.TextView$InputMethodState
 android.widget.TextView$OnEditorActionListener
 android.widget.TextView$SavedState
-android.widget.TextView$TextAlign
 android.widget.VideoView
 android.widget.ViewAnimator
 com.android.i18n.phonenumbers.AsYouTypeFormatter
@@ -1144,7 +1127,7 @@
 com.android.internal.telephony.ITelephonyRegistry
 com.android.internal.telephony.ITelephonyRegistry$Stub
 com.android.internal.telephony.ITelephonyRegistry$Stub$Proxy
-com.android.internal.telephony.Phone$State
+com.android.internal.telephony.PhoneConstants$State
 com.android.internal.util.ArrayUtils
 com.android.internal.util.FastXmlSerializer
 com.android.internal.util.Preconditions
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 38f4554..8e341df 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -177,7 +177,7 @@
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                onUserRemoved(intent.getIntExtra(Intent.EXTRA_USERID, -1));
+                onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
             }
         }, userFilter);
     }
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index 48f967c..5250dfc 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -43,7 +43,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.AtomicFile;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -593,7 +593,7 @@
     private boolean callerHasBindAppWidgetPermission(String packageName) {
         int callingUid = Binder.getCallingUid();
         try {
-            if (!UserId.isSameApp(callingUid, getUidForPackage(packageName))) {
+            if (!UserHandle.isSameApp(callingUid, getUidForPackage(packageName))) {
                 return false;
             }
         } catch (Exception e) {
@@ -665,7 +665,7 @@
                 mBoundRemoteViewsServices.remove(key);
             }
 
-            int userId = UserId.getUserId(id.provider.uid);
+            int userId = UserHandle.getUserId(id.provider.uid);
             // Bind to the RemoteViewsService (which will trigger a callback to the
             // RemoteViewsAdapter.onServiceConnected())
             final long token = Binder.clearCallingIdentity();
@@ -756,7 +756,7 @@
             }
         };
 
-        int userId = UserId.getUserId(id.provider.uid);
+        int userId = UserHandle.getUserId(id.provider.uid);
         // Bind to the service and remove the static intent->factory mapping in the
         // RemoteViewsService.
         final long token = Binder.clearCallingIdentity();
@@ -1026,7 +1026,7 @@
                             }
                         };
 
-                        int userId = UserId.getUserId(id.provider.uid);
+                        int userId = UserHandle.getUserId(id.provider.uid);
                         // Bind to the service and call onDataSetChanged()
                         final long token = Binder.clearCallingIdentity();
                         try {
@@ -1375,7 +1375,7 @@
             throw new IllegalArgumentException("packageName and uid don't match packageName="
                     + packageName);
         }
-        if (!UserId.isSameApp(callingUid, packageUid)) {
+        if (!UserHandle.isSameApp(callingUid, packageUid)) {
             throw new IllegalArgumentException("packageName and uid don't match packageName="
                     + packageName);
         }
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 2167c49..8be0ba8 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -65,6 +65,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.os.WorkSource;
 import android.os.storage.IMountService;
 import android.provider.Settings;
@@ -4845,6 +4846,18 @@
     // ----- IBackupManager binder interface -----
 
     public void dataChanged(final String packageName) {
+        final int callingUserHandle = UserHandle.getCallingUserId();
+        if (callingUserHandle != UserHandle.USER_OWNER) {
+            // App is running under a non-owner user profile.  For now, we do not back
+            // up data from secondary user profiles.
+            // TODO: backups for all user profiles.
+            if (MORE_DEBUG) {
+                Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
+                        + callingUserHandle);
+            }
+            return;
+        }
+
         final HashSet<String> targets = dataChangedTargets(packageName);
         if (targets == null) {
             Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
@@ -4937,6 +4950,11 @@
             boolean doAllApps, boolean includeSystem, String[] pkgList) {
         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
 
+        final int callingUserHandle = UserHandle.getCallingUserId();
+        if (callingUserHandle != UserHandle.USER_OWNER) {
+            throw new IllegalStateException("Backup supported only for the device owner");
+        }
+
         // Validate
         if (!doAllApps) {
             if (!includeShared) {
@@ -5001,6 +5019,11 @@
     public void fullRestore(ParcelFileDescriptor fd) {
         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore");
 
+        final int callingUserHandle = UserHandle.getCallingUserId();
+        if (callingUserHandle != UserHandle.USER_OWNER) {
+            throw new IllegalStateException("Restore supported only for the device owner");
+        }
+
         long oldId = Binder.clearCallingIdentity();
 
         try {
diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java
old mode 100644
new mode 100755
index cc9b9fa..4c98ac3
--- a/services/java/com/android/server/BluetoothManagerService.java
+++ b/services/java/com/android/server/BluetoothManagerService.java
@@ -22,6 +22,7 @@
 import android.os.Message;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.Binder;
 import android.provider.Settings;
 import android.util.Log;
 import java.util.List;
@@ -66,6 +67,7 @@
     private IBluetooth mBluetooth;
     private boolean mBinding;
     private boolean mUnbinding;
+    private boolean mQuietEnable = false;
 
     private void registerForAirplaneMode(IntentFilter filter) {
         final ContentResolver resolver = mContext.getContentResolver();
@@ -105,7 +107,7 @@
                 } else {
                     if (isBluetoothPersistedStateOn()) {
                         // enable without persisting the setting
-                        handleEnable(false);
+                        handleEnable(false, false);
                     }
                 }
             }
@@ -269,7 +271,32 @@
         Message msg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
         mHandler.sendMessage(msg);
     }
+    public boolean enableNoAutoConnect()
+    {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                "Need BLUETOOTH ADMIN permission");
+        if (DBG) {
+            Log.d(TAG,"enableNoAutoConnect():  mBluetooth =" + mBluetooth +
+                    " mBinding = " + mBinding);
+        }
+        if (Binder.getCallingUid() != android.os.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
+        mHandler.sendMessage(msg);
+        return true;
+
+    }
     public boolean enable() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH ADMIN permission");
@@ -288,6 +315,7 @@
 
         Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
         msg.arg1=1; //persist
+        msg.arg2=0; //No Quiet Mode
         mHandler.sendMessage(msg);
         return true;
     }
@@ -501,7 +529,7 @@
                         Log.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth);
                     }
 
-                    handleEnable(msg.arg1 == 1);
+                    handleEnable(msg.arg1 == 1, msg.arg2 ==1);
                     break;
 
                 case MESSAGE_DISABLE:
@@ -574,14 +602,21 @@
 
                         //Do enable request
                         try {
-                            if(!mBluetooth.enable()) {
-                                Log.e(TAG,"IBluetooth.enable() returned false");
+                            if (mQuietEnable == false) {
+                                if(!mBluetooth.enable()) {
+                                    Log.e(TAG,"IBluetooth.enable() returned false");
+                                }
+                            }
+                            else
+                            {
+                                if(!mBluetooth.enableNoAutoConnect()) {
+                                    Log.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
+                                }
                             }
                         } catch (RemoteException e) {
                             Log.e(TAG,"Unable to call enable()",e);
                         }
                     }
-
                     break;
                 }
                 case MESSAGE_TIMEOUT_BIND: {
@@ -637,11 +672,13 @@
         }
     };
 
-    private void handleEnable(boolean persist) {
+    private void handleEnable(boolean persist, boolean quietMode) {
         if (persist) {
             persistBluetoothSetting(true);
         }
 
+        mQuietEnable = quietMode;
+
         synchronized(mConnection) {
             if (mBluetooth == null) {
                 //Start bind timeout and bind
@@ -664,8 +701,15 @@
 
                 //Enable bluetooth
                 try {
-                    if(!mBluetooth.enable()) {
-                        Log.e(TAG,"IBluetooth.enable() returned false");
+                    if (!mQuietEnable) {
+                        if(!mBluetooth.enable()) {
+                            Log.e(TAG,"IBluetooth.enable() returned false");
+                        }
+                    }
+                    else {
+                        if(!mBluetooth.enableNoAutoConnect()) {
+                            Log.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
+                        }
                     }
                 } catch (RemoteException e) {
                     Log.e(TAG,"Unable to call enable()",e);
diff --git a/services/java/com/android/server/ClipboardService.java b/services/java/com/android/server/ClipboardService.java
index 8a6a550..0bf7aad 100644
--- a/services/java/com/android/server/ClipboardService.java
+++ b/services/java/com/android/server/ClipboardService.java
@@ -36,7 +36,7 @@
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -96,7 +96,7 @@
             public void onReceive(Context context, Intent intent) {
                 String action = intent.getAction();
                 if (Intent.ACTION_USER_REMOVED.equals(action)) {
-                    removeClipboard(intent.getIntExtra(Intent.EXTRA_USERID, 0));
+                    removeClipboard(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
                 }
             }
         }, userFilter);
@@ -115,7 +115,7 @@
     }
 
     private PerUserClipboard getClipboard() {
-        return getClipboard(UserId.getCallingUserId());
+        return getClipboard(UserHandle.getCallingUserId());
     }
 
     private PerUserClipboard getClipboard(int userId) {
@@ -258,7 +258,7 @@
         PackageInfo pi;
         try {
             pi = mPm.getPackageInfo(pkg, 0);
-            if (!UserId.isSameApp(pi.applicationInfo.uid, uid)) {
+            if (!UserHandle.isSameApp(pi.applicationInfo.uid, uid)) {
                 throw new SecurityException("Calling uid " + uid
                         + " does not own package " + pkg);
             }
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index ea19d6e..f966a33 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -1626,7 +1626,7 @@
             mLastMaximumTimeToLock = timeMs;
 
             try {
-                getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
+                getIPowerManager().setMaximumScreenOffTimeoutFromDeviceAdmin((int)timeMs);
             } catch (RemoteException e) {
                 Slog.w(TAG, "Failure talking with power manager", e);
             }
@@ -1667,8 +1667,8 @@
             long ident = Binder.clearCallingIdentity();
             try {
                 // Power off the display
-                mIPowerManager.goToSleepWithReason(SystemClock.uptimeMillis(),
-                        WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN);
+                getIPowerManager().goToSleep(SystemClock.uptimeMillis(),
+                        PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN);
                 // Ensure the device is locked
                 getWindowManager().lockNow();
             } catch (RemoteException e) {
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
index 6f050d3..8bdd7be 100644
--- a/services/java/com/android/server/DockObserver.java
+++ b/services/java/com/android/server/DockObserver.java
@@ -33,6 +33,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.UEventObserver;
 import android.provider.Settings;
@@ -64,11 +65,8 @@
 
     private final Context mContext;
 
-    private PowerManagerService mPowerManager;
-
-    public DockObserver(Context context, PowerManagerService pm) {
+    public DockObserver(Context context) {
         mContext = context;
-        mPowerManager = pm;
         init();  // set initial status
 
         startObserving(DOCK_UEVENT_MATCH);
@@ -94,8 +92,9 @@
                                 && mPreviousDockState != Intent.EXTRA_DOCK_STATE_LE_DESK
                                 && mPreviousDockState != Intent.EXTRA_DOCK_STATE_HE_DESK) ||
                                 mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
-                            mPowerManager.userActivityWithForce(SystemClock.uptimeMillis(),
-                                    false, true);
+                            PowerManager pm =
+                                    (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+                            pm.wakeUp(SystemClock.uptimeMillis());
                         }
                         update();
                     }
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index e219e8d..bb005d9 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -47,7 +47,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.Parcelable;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
@@ -64,6 +63,7 @@
 import com.android.server.location.GeocoderProxy;
 import com.android.server.location.GeofenceManager;
 import com.android.server.location.GpsLocationProvider;
+import com.android.server.location.LocationFudger;
 import com.android.server.location.LocationProviderInterface;
 import com.android.server.location.LocationProviderProxy;
 import com.android.server.location.MockProvider;
@@ -110,19 +110,6 @@
 
     private static final int MSG_LOCATION_CHANGED = 1;
 
-    // Accuracy in meters above which a location is considered coarse
-    private static final double COARSE_ACCURACY_M = 100.0;
-    private static final String EXTRA_COARSE_LOCATION = "coarseLocation";
-
-    private static final int APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR = 111000;
-
-    /**
-     * Maximum latitude of 1 meter from the pole.
-     * This keeps cosine(MAX_LATITUDE) to a non-zero value;
-     */
-    private static final double MAX_LATITUDE =
-            90.0 - (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR);
-
     // Location Providers may sometimes deliver location updates
     // slightly faster that requested - provide grace period so
     // we don't unnecessarily filter events that are otherwise on
@@ -137,6 +124,7 @@
     private final Object mLock = new Object();
 
     // --- fields below are final after init() ---
+    private LocationFudger mLocationFudger;
     private GeofenceManager mGeofenceManager;
     private PowerManager.WakeLock mWakeLock;
     private PackageManager mPackageManager;
@@ -221,6 +209,7 @@
             loadProvidersLocked();
         }
         mGeofenceManager = new GeofenceManager(mContext);
+        mLocationFudger = new LocationFudger();
 
         // Register for Network (Wifi or Mobile) updates
         IntentFilter filter = new IntentFilter();
@@ -907,7 +896,25 @@
         String perm = checkPermission();
 
         if (ACCESS_COARSE_LOCATION.equals(perm)) {
-            request.applyCoarsePermissionRestrictions();
+             switch (request.getQuality()) {
+                 case LocationRequest.ACCURACY_FINE:
+                     request.setQuality(LocationRequest.ACCURACY_BLOCK);
+                     break;
+                 case LocationRequest.POWER_HIGH:
+                     request.setQuality(LocationRequest.POWER_LOW);
+                     break;
+             }
+             // throttle
+             if (request.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
+                 request.setInterval(LocationFudger.FASTEST_INTERVAL_MS);
+             }
+             if (request.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) {
+                 request.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS);
+             }
+        }
+        // make getFastestInterval() the minimum of interval and fastest interval
+        if (request.getFastestInterval() > request.getInterval()) {
+            request.setFastestInterval(request.getInterval());
         }
         return perm;
     }
@@ -1075,7 +1082,7 @@
             if (ACCESS_FINE_LOCATION.equals(perm)) {
                 return location;
             } else {
-                return getCoarseLocationExtra(location);
+                return mLocationFudger.getOrCreate(location);
             }
         }
     }
@@ -1283,6 +1290,8 @@
     }
 
     private void handleLocationChangedLocked(Location location, boolean passive) {
+        if (D) Log.d(TAG, "incoming location: " + location);
+
         long now = SystemClock.elapsedRealtime();
         String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
         ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
@@ -1291,11 +1300,8 @@
         LocationProviderInterface p = mProvidersByName.get(provider);
         if (p == null) return;
 
-        // Add the coarse location as an extra, if not already present
-        Location coarse = getCoarseLocationExtra(location);
-        if (coarse == null) {
-            coarse = addCoarseLocationExtra(location);
-        }
+        // Add the coarse location as an extra
+        Location coarse = mLocationFudger.getOrCreate(location);
 
         // Update last known locations
         Location lastLocation = mLastLocation.get(provider);
@@ -1660,106 +1666,6 @@
         }
     }
 
-    private static double wrapLatitude(double lat) {
-         if (lat > MAX_LATITUDE) lat = MAX_LATITUDE;
-         if (lat < -MAX_LATITUDE) lat = -MAX_LATITUDE;
-         return lat;
-    }
-
-    private static double wrapLongitude(double lon) {
-        if (lon >= 180.0) lon -= 360.0;
-        if (lon < -180.0) lon += 360.0;
-        return lon;
-    }
-
-    private static double distanceToDegreesLatitude(double distance) {
-        return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR;
-    }
-
-    /**
-     * Requires latitude since longitudinal distances change with distance from equator.
-     */
-    private static double distanceToDegreesLongitude(double distance, double lat) {
-        return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR / Math.cos(lat);
-    }
-
-    /**
-     * Fudge a location into a coarse location.
-     * <p>Add a random offset, then quantize the result (snap-to-grid).
-     * Random offsets alone can be low-passed pretty easily.
-     * Snap-to-grid on its own is excellent unless you are sitting on a
-     * grid boundary and bouncing between quantizations.
-     * The combination is quite hard to reverse engineer.
-     * <p>The random offset used is smaller than the goal accuracy
-     * ({@link #COARSE_ACCURACY_M}), in order to give relatively stable
-     * results after quantization.
-     */
-    private static Location createCoarse(Location fine) {
-        Location coarse = new Location(fine);
-
-        coarse.removeBearing();
-        coarse.removeSpeed();
-        coarse.removeAltitude();
-
-        double lat = coarse.getLatitude();
-        double lon = coarse.getLongitude();
-
-        // wrap
-        lat = wrapLatitude(lat);
-        lon = wrapLongitude(lon);
-
-        if (coarse.getAccuracy() < COARSE_ACCURACY_M / 2) {
-            // apply a random offset
-            double fudgeDistance = COARSE_ACCURACY_M / 2.0 - coarse.getAccuracy();
-            lat += (Math.random() - 0.5) * distanceToDegreesLatitude(fudgeDistance);
-            lon += (Math.random() - 0.5) * distanceToDegreesLongitude(fudgeDistance, lat);
-        }
-
-        // wrap
-        lat = wrapLatitude(lat);
-        lon = wrapLongitude(lon);
-
-        // quantize (snap-to-grid)
-        double latGranularity = distanceToDegreesLatitude(COARSE_ACCURACY_M);
-        double lonGranularity = distanceToDegreesLongitude(COARSE_ACCURACY_M, lat);
-        long latQuantized = Math.round(lat / latGranularity);
-        long lonQuantized = Math.round(lon / lonGranularity);
-        lat = latQuantized * latGranularity;
-        lon = lonQuantized * lonGranularity;
-
-        // wrap again
-        lat = wrapLatitude(lat);
-        lon = wrapLongitude(lon);
-
-        // apply
-        coarse.setLatitude(lat);
-        coarse.setLongitude(lon);
-        coarse.setAccuracy((float)COARSE_ACCURACY_M);
-
-        return coarse;
-    }
-
-
-    private static Location getCoarseLocationExtra(Location location) {
-        Bundle extras = location.getExtras();
-        if (extras == null) return null;
-        Parcelable parcel = extras.getParcelable(EXTRA_COARSE_LOCATION);
-        if (parcel == null) return null;
-        if (!(parcel instanceof Location)) return null;
-        Location coarse = (Location) parcel;
-        if (coarse.getAccuracy() < COARSE_ACCURACY_M) return null;
-        return coarse;
-    }
-
-    private static Location addCoarseLocationExtra(Location location) {
-        Bundle extras = location.getExtras();
-        if (extras == null) extras = new Bundle();
-        Location coarse = createCoarse(location);
-        extras.putParcelable(EXTRA_COARSE_LOCATION, coarse);
-        location.setExtras(extras);
-        return coarse;
-    }
-
     private void log(String log) {
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Slog.d(TAG, log);
@@ -1819,6 +1725,9 @@
                 }
             }
 
+            pw.append("  fudger: ");
+            mLocationFudger.dump(fd, pw,  args);
+
             if (args.length > 0 && "short".equals(args[0])) {
                 return;
             }
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 04267a3..bb5d552 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -48,7 +48,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.os.storage.IMountService;
 import android.os.storage.IMountServiceListener;
 import android.os.storage.IMountShutdownObserver;
@@ -1713,7 +1713,7 @@
             return false;
         }
 
-        final int packageUid = mPms.getPackageUid(packageName, UserId.getUserId(callerUid));
+        final int packageUid = mPms.getPackageUid(packageName, UserHandle.getUserId(callerUid));
 
         if (DEBUG_OBB) {
             Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 9ab1a9d..a565d08 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -48,9 +48,10 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.os.Vibrator;
 import android.provider.Settings;
+import android.service.dreams.IDreamManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.AtomicFile;
@@ -148,6 +149,8 @@
     private AtomicFile mPolicyFile;
     private HashSet<String> mBlockedPackages = new HashSet<String>();
 
+    private IDreamManager mSandman;
+
     private static final int DB_VERSION = 1;
 
     private static final String TAG_BODY = "notification-policy";
@@ -634,6 +637,8 @@
     void systemReady() {
         mAudioService = IAudioService.Stub.asInterface(
                 ServiceManager.getService(Context.AUDIO_SERVICE));
+        mSandman = IDreamManager.Stub.asInterface(
+                ServiceManager.getService("dreams"));
 
         // no beeping until we're basically done booting
         mSystemReady = true;
@@ -972,6 +977,16 @@
                         | Notification.FLAG_NO_CLEAR;
             }
 
+            // Stop screensaver if the notification has a full-screen intent.
+            // (like an incoming phone call)
+            if (notification.fullScreenIntent != null && mSandman != null) {
+                try {
+                    mSandman.awaken();
+                } catch (RemoteException e) {
+                    // noop
+                }
+            }
+
             if (notification.icon != 0) {
                 StatusBarNotification n = new StatusBarNotification(pkg, id, tag,
                         r.uid, r.initialPid, score, notification);
@@ -1271,7 +1286,7 @@
         try {
             ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(
                     pkg, 0);
-            if (!UserId.isSameApp(ai.uid, uid)) {
+            if (!UserHandle.isSameApp(ai.uid, uid)) {
                 throw new SecurityException("Calling uid " + uid + " gave package"
                         + pkg + " which is owned by uid " + ai.uid);
             }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e8350c1..c471dd2 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -50,6 +50,7 @@
 import com.android.internal.widget.LockSettingsService;
 import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.am.ActivityManagerService;
+import com.android.server.am.BatteryStatsService;
 import com.android.server.display.DisplayManagerService;
 import com.android.server.input.InputManagerService;
 import com.android.server.net.NetworkPolicyManagerService;
@@ -230,7 +231,8 @@
 
             // only initialize the power service after we have started the
             // lights service, content providers and the battery service.
-            power.init(context, lights, ActivityManagerService.self(), battery, display);
+            power.init(context, lights, ActivityManagerService.self(), battery,
+                    BatteryStatsService.getService(), display);
 
             Slog.i(TAG, "Alarm Manager");
             alarm = new AlarmManagerService(context);
@@ -553,7 +555,7 @@
             try {
                 Slog.i(TAG, "Dock Observer");
                 // Listen for dock station changes
-                dock = new DockObserver(context, power);
+                dock = new DockObserver(context);
             } catch (Throwable e) {
                 reportWtf("starting DockObserver", e);
             }
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index d97d335..1d248b2 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -45,9 +45,10 @@
 import android.os.FileObserver;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteCallbackList;
+import android.os.SELinux;
 import android.os.ServiceManager;
 import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.service.wallpaper.IWallpaperConnection;
 import android.service.wallpaper.IWallpaperEngine;
 import android.service.wallpaper.IWallpaperService;
@@ -422,9 +423,9 @@
             public void onReceive(Context context, Intent intent) {
                 String action = intent.getAction();
                 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                    switchUser(intent.getIntExtra(Intent.EXTRA_USERID, 0));
+                    switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
                 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
-                    removeUser(intent.getIntExtra(Intent.EXTRA_USERID, 0));
+                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
                 }
             }
         }, userFilter);
@@ -483,7 +484,7 @@
     public void clearWallpaper() {
         if (DEBUG) Slog.v(TAG, "clearWallpaper");
         synchronized (mLock) {
-            clearWallpaperLocked(false, UserId.getCallingUserId());
+            clearWallpaperLocked(false, UserHandle.getCallingUserId());
         }
     }
 
@@ -520,7 +521,7 @@
     public void setDimensionHints(int width, int height) throws RemoteException {
         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
 
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
         WallpaperData wallpaper = mWallpaperMap.get(userId);
         if (wallpaper == null) {
             throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
@@ -551,14 +552,14 @@
 
     public int getWidthHint() throws RemoteException {
         synchronized (mLock) {
-            WallpaperData wallpaper = mWallpaperMap.get(UserId.getCallingUserId());
+            WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
             return wallpaper.width;
         }
     }
 
     public int getHeightHint() throws RemoteException {
         synchronized (mLock) {
-            WallpaperData wallpaper = mWallpaperMap.get(UserId.getCallingUserId());
+            WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
             return wallpaper.height;
         }
     }
@@ -573,7 +574,7 @@
             if (callingUid == android.os.Process.SYSTEM_UID) {
                 wallpaperUserId = mCurrentUserId;
             } else {
-                wallpaperUserId = UserId.getUserId(callingUid);
+                wallpaperUserId = UserHandle.getUserId(callingUid);
             }
             WallpaperData wallpaper = mWallpaperMap.get(wallpaperUserId);
             try {
@@ -596,7 +597,7 @@
     }
 
     public WallpaperInfo getWallpaperInfo() {
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
         synchronized (mLock) {
             WallpaperData wallpaper = mWallpaperMap.get(userId);
             if (wallpaper.connection != null) {
@@ -608,7 +609,7 @@
 
     public ParcelFileDescriptor setWallpaper(String name) {
         if (DEBUG) Slog.v(TAG, "setWallpaper");
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
         WallpaperData wallpaper = mWallpaperMap.get(userId);
         if (wallpaper == null) {
             throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
@@ -639,8 +640,12 @@
                         FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
                         -1, -1);
             }
-            ParcelFileDescriptor fd = ParcelFileDescriptor.open(new File(dir, WALLPAPER),
+            File file = new File(dir, WALLPAPER);
+            ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
                     MODE_CREATE|MODE_READ_WRITE);
+            if (!SELinux.restorecon(file)) {
+                return null;
+            }
             wallpaper.name = name;
             return fd;
         } catch (FileNotFoundException e) {
@@ -651,7 +656,7 @@
 
     public void setWallpaperComponent(ComponentName name) {
         if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
         WallpaperData wallpaper = mWallpaperMap.get(userId);
         if (wallpaper == null) {
             throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index c5b4a07..9edfad6 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -349,7 +349,7 @@
         }
 
         if (mMinScreenOff >= 0 && (mPower == null ||
-                mPower.timeSinceScreenOn() < mMinScreenOff)) {
+                mPower.timeSinceScreenWasLastOn() < mMinScreenOff)) {
             return "screen";
         }
 
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index e1c05b5..edbc624 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -303,6 +303,10 @@
                     mWifiStateMachine.sendMessage(Message.obtain(msg));
                     break;
                 }
+                case WifiManager.RSSI_PKTCNT_FETCH: {
+                    mWifiStateMachine.sendMessage(Message.obtain(msg));
+                    break;
+                }
                 default: {
                     Slog.d(TAG, "WifiServicehandler.handleMessage ignoring msg=" + msg);
                     break;
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index 533c2cd..d8ccabb 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -51,7 +51,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
@@ -226,7 +226,7 @@
 
         ServiceLookupResult res =
             retrieveServiceLocked(service, resolvedType,
-                    callingPid, callingUid, UserId.getUserId(callingUid));
+                    callingPid, callingUid, UserHandle.getUserId(callingUid), true);
         if (res == null) {
             return null;
         }
@@ -277,8 +277,10 @@
         }
 
         // If this service is active, make sure it is stopped.
-        ServiceLookupResult r = findServiceLocked(service, resolvedType,
-                callerApp == null ? UserId.getCallingUserId() : callerApp.userId);
+        ServiceLookupResult r = retrieveServiceLocked(service, resolvedType,
+                Binder.getCallingPid(), Binder.getCallingUid(),
+                callerApp == null ? UserHandle.getCallingUserId() : callerApp.userId,
+                false);
         if (r != null) {
             if (r.record != null) {
                 final long origId = Binder.clearCallingIdentity();
@@ -296,8 +298,9 @@
     }
 
     IBinder peekServiceLocked(Intent service, String resolvedType) {
-        ServiceLookupResult r = findServiceLocked(service, resolvedType,
-                UserId.getCallingUserId());
+        ServiceLookupResult r = retrieveServiceLocked(service, resolvedType,
+                Binder.getCallingPid(), Binder.getCallingUid(),
+                UserHandle.getCallingUserId(), false);
 
         IBinder ret = null;
         if (r != null) {
@@ -471,7 +474,7 @@
 
         ServiceLookupResult res =
             retrieveServiceLocked(service, resolvedType,
-                    Binder.getCallingPid(), Binder.getCallingUid(), userId);
+                    Binder.getCallingPid(), Binder.getCallingUid(), userId, true);
         if (res == null) {
             return 0;
         }
@@ -482,7 +485,7 @@
                 res.record.serviceInfo.name, res.record.serviceInfo.flags)) {
             userId = 0;
             res = retrieveServiceLocked(service, resolvedType, Binder.getCallingPid(),
-                    Binder.getCallingUid(), 0);
+                    Binder.getCallingUid(), 0, true);
         }
         ServiceRecord s = res.record;
 
@@ -689,60 +692,6 @@
             record = _record;
             permission = _permission;
         }
-    };
-
-    private ServiceLookupResult findServiceLocked(Intent service,
-            String resolvedType, int userId) {
-        ServiceRecord r = null;
-        if (service.getComponent() != null) {
-            r = mServiceMap.getServiceByName(service.getComponent(), userId);
-        }
-        if (r == null) {
-            Intent.FilterComparison filter = new Intent.FilterComparison(service);
-            r = mServiceMap.getServiceByIntent(filter, userId);
-        }
-
-        if (r == null) {
-            try {
-                ResolveInfo rInfo =
-                    AppGlobals.getPackageManager().resolveService(
-                                service, resolvedType, 0, userId);
-                ServiceInfo sInfo =
-                    rInfo != null ? rInfo.serviceInfo : null;
-                if (sInfo == null) {
-                    return null;
-                }
-
-                ComponentName name = new ComponentName(
-                        sInfo.applicationInfo.packageName, sInfo.name);
-                r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
-            } catch (RemoteException ex) {
-                // pm is in same process, this will never happen.
-            }
-        }
-        if (r != null) {
-            int callingPid = Binder.getCallingPid();
-            int callingUid = Binder.getCallingUid();
-            if (mAm.checkComponentPermission(r.permission,
-                    callingPid, callingUid, r.appInfo.uid, r.exported)
-                    != PackageManager.PERMISSION_GRANTED) {
-                if (!r.exported) {
-                    Slog.w(TAG, "Permission Denial: Accessing service " + r.name
-                            + " from pid=" + callingPid
-                            + ", uid=" + callingUid
-                            + " that is not exported from uid " + r.appInfo.uid);
-                    return new ServiceLookupResult(null, "not exported from uid "
-                            + r.appInfo.uid);
-                }
-                Slog.w(TAG, "Permission Denial: Accessing service " + r.name
-                        + " from pid=" + callingPid
-                        + ", uid=" + callingUid
-                        + " requires " + r.permission);
-                return new ServiceLookupResult(null, r.permission);
-            }
-            return new ServiceLookupResult(r, null);
-        }
-        return null;
     }
 
     private class ServiceRestarter implements Runnable {
@@ -760,7 +709,8 @@
     }
 
     private ServiceLookupResult retrieveServiceLocked(Intent service,
-            String resolvedType, int callingPid, int callingUid, int userId) {
+            String resolvedType, int callingPid, int callingUid, int userId,
+            boolean createIfNeeded) {
         ServiceRecord r = null;
         if (DEBUG_SERVICE) Slog.v(TAG, "retrieveServiceLocked: " + service
                 + " type=" + resolvedType + " callingUid=" + callingUid);
@@ -796,7 +746,7 @@
                     sInfo.applicationInfo = mAm.getAppInfoForUser(sInfo.applicationInfo, userId);
                 }
                 r = mServiceMap.getServiceByName(name, userId);
-                if (r == null) {
+                if (r == null && createIfNeeded) {
                     Intent.FilterComparison filter = new Intent.FilterComparison(
                             service.cloneFilter());
                     ServiceRestarter res = new ServiceRestarter();
@@ -809,8 +759,8 @@
                     }
                     r = new ServiceRecord(mAm, ss, name, filter, sInfo, res);
                     res.setService(r);
-                    mServiceMap.putServiceByName(name, UserId.getUserId(r.appInfo.uid), r);
-                    mServiceMap.putServiceByIntent(filter, UserId.getUserId(r.appInfo.uid), r);
+                    mServiceMap.putServiceByName(name, UserHandle.getUserId(r.appInfo.uid), r);
+                    mServiceMap.putServiceByIntent(filter, UserHandle.getUserId(r.appInfo.uid), r);
 
                     // Make sure this component isn't in the pending list.
                     int N = mPendingServices.size();
@@ -897,87 +847,94 @@
         boolean canceled = false;
 
         final long now = SystemClock.uptimeMillis();
-        long minDuration = SERVICE_RESTART_DURATION;
-        long resetTime = SERVICE_RESET_RUN_DURATION;
 
         if ((r.serviceInfo.applicationInfo.flags
-                &ApplicationInfo.FLAG_PERSISTENT) != 0) {
-            minDuration /= 4;
-        }
+                &ApplicationInfo.FLAG_PERSISTENT) == 0) {
+            long minDuration = SERVICE_RESTART_DURATION;
+            long resetTime = SERVICE_RESET_RUN_DURATION;
 
-        // Any delivered but not yet finished starts should be put back
-        // on the pending list.
-        final int N = r.deliveredStarts.size();
-        if (N > 0) {
-            for (int i=N-1; i>=0; i--) {
-                ServiceRecord.StartItem si = r.deliveredStarts.get(i);
-                si.removeUriPermissionsLocked();
-                if (si.intent == null) {
-                    // We'll generate this again if needed.
-                } else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT
-                        && si.doneExecutingCount < ServiceRecord.MAX_DONE_EXECUTING_COUNT)) {
-                    r.pendingStarts.add(0, si);
-                    long dur = SystemClock.uptimeMillis() - si.deliveredTime;
-                    dur *= 2;
-                    if (minDuration < dur) minDuration = dur;
-                    if (resetTime < dur) resetTime = dur;
-                } else {
-                    Slog.w(TAG, "Canceling start item " + si.intent + " in service "
-                            + r.name);
-                    canceled = true;
+            // Any delivered but not yet finished starts should be put back
+            // on the pending list.
+            final int N = r.deliveredStarts.size();
+            if (N > 0) {
+                for (int i=N-1; i>=0; i--) {
+                    ServiceRecord.StartItem si = r.deliveredStarts.get(i);
+                    si.removeUriPermissionsLocked();
+                    if (si.intent == null) {
+                        // We'll generate this again if needed.
+                    } else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT
+                            && si.doneExecutingCount < ServiceRecord.MAX_DONE_EXECUTING_COUNT)) {
+                        r.pendingStarts.add(0, si);
+                        long dur = SystemClock.uptimeMillis() - si.deliveredTime;
+                        dur *= 2;
+                        if (minDuration < dur) minDuration = dur;
+                        if (resetTime < dur) resetTime = dur;
+                    } else {
+                        Slog.w(TAG, "Canceling start item " + si.intent + " in service "
+                                + r.name);
+                        canceled = true;
+                    }
                 }
+                r.deliveredStarts.clear();
             }
-            r.deliveredStarts.clear();
-        }
 
-        r.totalRestartCount++;
-        if (r.restartDelay == 0) {
-            r.restartCount++;
-            r.restartDelay = minDuration;
-        } else {
-            // If it has been a "reasonably long time" since the service
-            // was started, then reset our restart duration back to
-            // the beginning, so we don't infinitely increase the duration
-            // on a service that just occasionally gets killed (which is
-            // a normal case, due to process being killed to reclaim memory).
-            if (now > (r.restartTime+resetTime)) {
-                r.restartCount = 1;
+            r.totalRestartCount++;
+            if (r.restartDelay == 0) {
+                r.restartCount++;
                 r.restartDelay = minDuration;
             } else {
-                if ((r.serviceInfo.applicationInfo.flags
-                        &ApplicationInfo.FLAG_PERSISTENT) != 0) {
-                    // Services in peristent processes will restart much more
-                    // quickly, since they are pretty important.  (Think SystemUI).
-                    r.restartDelay += minDuration/2;
+                // If it has been a "reasonably long time" since the service
+                // was started, then reset our restart duration back to
+                // the beginning, so we don't infinitely increase the duration
+                // on a service that just occasionally gets killed (which is
+                // a normal case, due to process being killed to reclaim memory).
+                if (now > (r.restartTime+resetTime)) {
+                    r.restartCount = 1;
+                    r.restartDelay = minDuration;
                 } else {
-                    r.restartDelay *= SERVICE_RESTART_DURATION_FACTOR;
-                    if (r.restartDelay < minDuration) {
-                        r.restartDelay = minDuration;
+                    if ((r.serviceInfo.applicationInfo.flags
+                            &ApplicationInfo.FLAG_PERSISTENT) != 0) {
+                        // Services in peristent processes will restart much more
+                        // quickly, since they are pretty important.  (Think SystemUI).
+                        r.restartDelay += minDuration/2;
+                    } else {
+                        r.restartDelay *= SERVICE_RESTART_DURATION_FACTOR;
+                        if (r.restartDelay < minDuration) {
+                            r.restartDelay = minDuration;
+                        }
                     }
                 }
             }
-        }
 
-        r.nextRestartTime = now + r.restartDelay;
+            r.nextRestartTime = now + r.restartDelay;
 
-        // Make sure that we don't end up restarting a bunch of services
-        // all at the same time.
-        boolean repeat;
-        do {
-            repeat = false;
-            for (int i=mRestartingServices.size()-1; i>=0; i--) {
-                ServiceRecord r2 = mRestartingServices.get(i);
-                if (r2 != r && r.nextRestartTime
-                        >= (r2.nextRestartTime-SERVICE_MIN_RESTART_TIME_BETWEEN)
-                        && r.nextRestartTime
-                        < (r2.nextRestartTime+SERVICE_MIN_RESTART_TIME_BETWEEN)) {
-                    r.nextRestartTime = r2.nextRestartTime + SERVICE_MIN_RESTART_TIME_BETWEEN;
-                    r.restartDelay = r.nextRestartTime - now;
-                    repeat = true;
-                    break;
+            // Make sure that we don't end up restarting a bunch of services
+            // all at the same time.
+            boolean repeat;
+            do {
+                repeat = false;
+                for (int i=mRestartingServices.size()-1; i>=0; i--) {
+                    ServiceRecord r2 = mRestartingServices.get(i);
+                    if (r2 != r && r.nextRestartTime
+                            >= (r2.nextRestartTime-SERVICE_MIN_RESTART_TIME_BETWEEN)
+                            && r.nextRestartTime
+                            < (r2.nextRestartTime+SERVICE_MIN_RESTART_TIME_BETWEEN)) {
+                        r.nextRestartTime = r2.nextRestartTime + SERVICE_MIN_RESTART_TIME_BETWEEN;
+                        r.restartDelay = r.nextRestartTime - now;
+                        repeat = true;
+                        break;
+                    }
                 }
-            }
-        } while (repeat);
+            } while (repeat);
+
+        } else {
+            // Persistent processes are immediately restrted, so there is no
+            // reason to hold of on restarting their services.
+            r.totalRestartCount++;
+            r.restartCount = 0;
+            r.restartDelay = 0;
+            r.nextRestartTime = now;
+        }
 
         if (!mRestartingServices.contains(r)) {
             mRestartingServices.add(r);
@@ -1494,6 +1451,7 @@
 
     boolean attachApplicationLocked(ProcessRecord proc, String processName) throws Exception {
         boolean didSomething = false;
+        // Collect any services that are waiting for this process to come up.
         if (mPendingServices.size() > 0) {
             ServiceRecord sr = null;
             try {
@@ -1515,6 +1473,22 @@
                 throw e;
             }
         }
+        // Also, if there are any services that are waiting to restart and
+        // would run in this process, now is a good time to start them.  It would
+        // be weird to bring up the process but arbitrarily not let the services
+        // run at this point just because their restart time hasn't come up.
+        if (mRestartingServices.size() > 0) {
+            ServiceRecord sr = null;
+            for (int i=0; i<mRestartingServices.size(); i++) {
+                sr = mRestartingServices.get(i);
+                if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
+                        || !processName.equals(sr.processName))) {
+                    continue;
+                }
+                mAm.mHandler.removeCallbacks(sr.restarter);
+                mAm.mHandler.post(sr.restarter);
+            }
+        }
         return didSomething;
     }
 
@@ -1751,7 +1725,7 @@
         ArrayList<ActivityManager.RunningServiceInfo> res
                 = new ArrayList<ActivityManager.RunningServiceInfo>();
 
-        int userId = UserId.getUserId(Binder.getCallingUid());
+        int userId = UserHandle.getUserId(Binder.getCallingUid());
         if (mServiceMap.getAllServices(userId).size() > 0) {
             Iterator<ServiceRecord> it
                     = mServiceMap.getAllServices(userId).iterator();
@@ -1772,7 +1746,7 @@
     }
 
     public PendingIntent getRunningServiceControlPanelLocked(ComponentName name) {
-        int userId = UserId.getUserId(Binder.getCallingUid());
+        int userId = UserHandle.getUserId(Binder.getCallingUid());
         ServiceRecord r = mServiceMap.getServiceByName(name, userId);
         if (r != null) {
             for (ArrayList<ConnectionRecord> conn : r.connections.values()) {
@@ -1836,7 +1810,8 @@
         pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)");
         try {
             List<UserInfo> users = mAm.getUserManager().getUsers();
-            for (UserInfo user : users) {
+            for (int ui=0; ui<users.size(); ui++) {
+                final UserInfo user = users.get(ui);
                 if (mServiceMap.getAllServices(user.id).size() > 0) {
                     boolean printed = false;
                     long nowReal = SystemClock.elapsedRealtime();
@@ -1852,7 +1827,10 @@
                             continue;
                         }
                         if (!printed) {
-                            pw.println("  Active services:");
+                            if (ui > 0) {
+                                pw.println();
+                            }
+                            pw.println("  User " + user.id + " active services:");
                             printed = true;
                         }
                         if (needSep) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index d3ec9f7..d36114c 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -111,7 +111,7 @@
 import android.os.StrictMode;
 import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.text.format.Time;
@@ -686,6 +686,18 @@
     int mLruSeq = 0;
 
     /**
+     * Keep track of the non-hidden/empty process we last found, to help
+     * determine how to distribute hidden/empty processes next time.
+     */
+    int mNumNonHiddenProcs = 0;
+
+    /**
+     * Keep track of the number of hidden procs, to balance oom adj
+     * distribution between those and empty procs.
+     */
+    int mNumHiddenProcs = 0;
+
+    /**
      * Keep track of the number of service processes we last found, to
      * determine on the next iteration which should be B services.
      */
@@ -1072,7 +1084,7 @@
                     boolean restart = (msg.arg2 == 1);
                     String pkg = (String) msg.obj;
                     forceStopPackageLocked(pkg, uid, restart, false, true, false,
-                            UserId.getUserId(uid));
+                            UserHandle.getUserId(uid));
                 }
             } break;
             case FINALIZE_PENDING_INTENT_MSG: {
@@ -1817,7 +1829,7 @@
             if (procs == null) return null;
             final int N = procs.size();
             for (int i = 0; i < N; i++) {
-                if (UserId.isSameUser(procs.keyAt(i), uid)) return procs.valueAt(i);
+                if (UserHandle.isSameUser(procs.keyAt(i), uid)) return procs.valueAt(i);
             }
         }
         ProcessRecord proc = mProcessNames.get(processName, uid);
@@ -1952,7 +1964,7 @@
                 mPidsSelfLocked.remove(app.pid);
                 mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
             }
-            app.pid = 0;
+            app.setPid(0);
         }
 
         if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG,
@@ -1968,10 +1980,20 @@
             int uid = app.uid;
 
             int[] gids = null;
+            int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
             if (!app.isolated) {
                 try {
-                    gids = mContext.getPackageManager().getPackageGids(
-                            app.info.packageName);
+                    final PackageManager pm = mContext.getPackageManager();
+                    gids = pm.getPackageGids(app.info.packageName);
+                    if (pm.checkPermission(
+                            android.Manifest.permission.READ_EXTERNAL_STORAGE, app.info.packageName)
+                            == PERMISSION_GRANTED) {
+                        if (Environment.isExternalStorageEmulated()) {
+                            mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
+                        } else {
+                            mountExternal = Zygote.MOUNT_EXTERNAL_SINGLEUSER;
+                        }
+                    }
                 } catch (PackageManager.NameNotFoundException e) {
                     Slog.w(TAG, "Unable to retrieve gids", e);
                 }
@@ -2013,7 +2035,7 @@
             // Start the process.  It will either succeed and return a result containing
             // the PID of the new process, or else throw a RuntimeException.
             Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
-                    app.processName, uid, uid, gids, debugFlags,
+                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                     app.info.targetSdkVersion, null, null);
 
             BatteryStatsImpl bs = app.batteryStats.getBatteryStats();
@@ -2055,7 +2077,7 @@
             }
             buf.append("}");
             Slog.i(TAG, buf.toString());
-            app.pid = startResult.pid;
+            app.setPid(startResult.pid);
             app.usingWrapper = startResult.usingWrapper;
             app.removed = false;
             synchronized (mPidsSelfLocked) {
@@ -2067,7 +2089,7 @@
             }
         } catch (RuntimeException e) {
             // XXX do better error recovery.
-            app.pid = 0;
+            app.setPid(0);
             Slog.e(TAG, "Failure starting process " + app.processName, e);
         }
     }
@@ -2183,7 +2205,7 @@
     }
 
     void enforceNotIsolatedCaller(String caller) {
-        if (UserId.isIsolated(Binder.getCallingUid())) {
+        if (UserHandle.isIsolated(Binder.getCallingUid())) {
             throw new SecurityException("Isolated process not allowed to call " + caller);
         }
     }
@@ -2312,7 +2334,7 @@
             String resultWho, int requestCode, int startFlags,
             String profileFile, ParcelFileDescriptor profileFd, Bundle options) {
         return startActivityAsUser(caller, intent, resolvedType, resultTo, resultWho, requestCode,
-                startFlags, profileFile, profileFd, options, UserId.getCallingUserId());
+                startFlags, profileFile, profileFd, options, UserHandle.getCallingUserId());
     }
 
     public final int startActivityAsUser(IApplicationThread caller,
@@ -2320,20 +2342,20 @@
             String resultWho, int requestCode, int startFlags,
             String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) {
         enforceNotIsolatedCaller("startActivity");
-        if (userId != UserId.getCallingUserId()) {
+        if (userId != UserHandle.getCallingUserId()) {
             // Requesting a different user, make sure that they have the permission
             if (checkComponentPermission(
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                     Binder.getCallingPid(), Binder.getCallingUid(), -1, true)
                     == PackageManager.PERMISSION_GRANTED) {
                 // Translate to the current user id, if caller wasn't aware
-                if (userId == UserId.USER_CURRENT) {
+                if (userId == UserHandle.USER_CURRENT) {
                     userId = mCurrentUserId;
                 }
             } else {
                 String msg = "Permission Denial: "
                         + "Request to startActivity as user " + userId
-                        + " but is calling from user " + UserId.getCallingUserId()
+                        + " but is calling from user " + UserHandle.getCallingUserId()
                         + "; this requires "
                         + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
                 Slog.w(TAG, msg);
@@ -2445,7 +2467,7 @@
                     AppGlobals.getPackageManager().queryIntentActivities(
                             intent, r.resolvedType,
                             PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS,
-                            UserId.getCallingUserId());
+                            UserHandle.getCallingUserId());
 
                 // Look for the original activity in the list...
                 final int N = resolves != null ? resolves.size() : 0;
@@ -2550,7 +2572,7 @@
                     "startActivityInPackage only available to the system");
         }
         int ret = mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo,
-                options, UserId.getUserId(uid));
+                options, UserHandle.getUserId(uid));
         return ret;
     }
 
@@ -3416,7 +3438,7 @@
             throw new SecurityException(msg);
         }
         
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
         long callingId = Binder.clearCallingIdentity();
         try {
             IPackageManager pm = AppGlobals.getPackageManager();
@@ -3490,7 +3512,7 @@
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
-        final int userId = UserId.getCallingUserId();
+        final int userId = UserHandle.getCallingUserId();
         long callingId = Binder.clearCallingIdentity();
         try {
             IPackageManager pm = AppGlobals.getPackageManager();
@@ -3626,7 +3648,7 @@
     }
 
     private void forceStopPackageLocked(final String packageName, int uid) {
-        forceStopPackageLocked(packageName, uid, false, false, true, false, UserId.getUserId(uid));
+        forceStopPackageLocked(packageName, uid, false, false, true, false, UserHandle.getUserId(uid));
         Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
                 Uri.fromParts("package", packageName, null));
         if (!mProcessesReady) {
@@ -3636,7 +3658,7 @@
         broadcastIntentLocked(null, null, intent,
                 null, null, 0, null, null, null,
                 false, false,
-                MY_PID, Process.SYSTEM_UID, UserId.getUserId(uid));
+                MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid));
     }
     
     private final boolean killPackageProcessesLocked(String packageName, int uid,
@@ -4357,8 +4379,8 @@
             try {
                 if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
                     int uid = AppGlobals.getPackageManager()
-                            .getPackageUid(packageName, UserId.getUserId(callingUid));
-                    if (!UserId.isSameApp(callingUid, uid)) {
+                            .getPackageUid(packageName, UserHandle.getUserId(callingUid));
+                    if (!UserHandle.isSameApp(callingUid, uid)) {
                         String msg = "Permission Denial: getIntentSender() from pid="
                             + Binder.getCallingPid()
                             + ", uid=" + Binder.getCallingUid()
@@ -4454,8 +4476,8 @@
             PendingIntentRecord rec = (PendingIntentRecord)sender;
             try {
                 int uid = AppGlobals.getPackageManager()
-                        .getPackageUid(rec.key.packageName, UserId.getCallingUserId());
-                if (!UserId.isSameApp(uid, Binder.getCallingUid())) {
+                        .getPackageUid(rec.key.packageName, UserHandle.getCallingUserId());
+                if (!UserHandle.isSameApp(uid, Binder.getCallingUid())) {
                     String msg = "Permission Denial: cancelIntentSender() from pid="
                         + Binder.getCallingPid()
                         + ", uid=" + Binder.getCallingUid()
@@ -4674,7 +4696,7 @@
         if (permission == null) {
             return PackageManager.PERMISSION_DENIED;
         }
-        return checkComponentPermission(permission, pid, UserId.getAppId(uid), -1, true);
+        return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true);
     }
 
     /**
@@ -4684,7 +4706,7 @@
     int checkCallingPermission(String permission) {
         return checkPermission(permission,
                 Binder.getCallingPid(),
-                UserId.getAppId(Binder.getCallingUid()));
+                UserHandle.getAppId(Binder.getCallingUid()));
     }
 
     /**
@@ -4815,7 +4837,7 @@
             pid = tlsIdentity.pid;
         }
 
-        uid = UserId.getAppId(uid);
+        uid = UserHandle.getAppId(uid);
         // Our own process gets to do everything.
         if (pid == MY_PID) {
             return PackageManager.PERMISSION_GRANTED;
@@ -4861,13 +4883,13 @@
         String name = uri.getAuthority();
         ProviderInfo pi = null;
         ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
-                UserId.getUserId(callingUid));
+                UserHandle.getUserId(callingUid));
         if (cpr != null) {
             pi = cpr.info;
         } else {
             try {
                 pi = pm.resolveContentProvider(name,
-                        PackageManager.GET_URI_PERMISSION_PATTERNS, UserId.getUserId(callingUid));
+                        PackageManager.GET_URI_PERMISSION_PATTERNS, UserHandle.getUserId(callingUid));
             } catch (RemoteException ex) {
             }
         }
@@ -4879,7 +4901,7 @@
         int targetUid = lastTargetUid;
         if (targetUid < 0 && targetPkg != null) {
             try {
-                targetUid = pm.getPackageUid(targetPkg, UserId.getUserId(callingUid));
+                targetUid = pm.getPackageUid(targetPkg, UserHandle.getUserId(callingUid));
                 if (targetUid < 0) {
                     if (DEBUG_URI_PERMISSION) Slog.v(TAG,
                             "Can't grant URI permission no uid for: " + targetPkg);
@@ -5171,7 +5193,7 @@
 
         final String authority = uri.getAuthority();
         ProviderInfo pi = null;
-        int userId = UserId.getUserId(callingUid);
+        int userId = UserHandle.getUserId(callingUid);
         ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userId);
         if (cpr != null) {
             pi = cpr.info;
@@ -5509,7 +5531,7 @@
     public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
             int flags, int userId) {
         final int callingUid = Binder.getCallingUid();
-        if (userId != UserId.getCallingUserId()) {
+        if (userId != UserHandle.getCallingUserId()) {
             // Check if the caller is holding permissions for cross-user requests.
             if (checkComponentPermission(
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
@@ -5517,13 +5539,13 @@
                     != PackageManager.PERMISSION_GRANTED) {
                 String msg = "Permission Denial: "
                         + "Request to get recent tasks for user " + userId
-                        + " but is calling from user " + UserId.getUserId(callingUid)
+                        + " but is calling from user " + UserHandle.getUserId(callingUid)
                         + "; this requires "
                         + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
                 Slog.w(TAG, msg);
                 throw new SecurityException(msg);
             } else {
-                if (userId == UserId.USER_CURRENT) {
+                if (userId == UserHandle.USER_CURRENT) {
                     userId = mCurrentUserId;
                 }
             }
@@ -5994,7 +6016,7 @@
                     (ProviderInfo)providers.get(i);
                 boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
                         cpi.name, cpi.flags);
-                if (singleton && UserId.getUserId(app.uid) != 0) {
+                if (singleton && UserHandle.getUserId(app.uid) != 0) {
                     // This is a singleton provider, but a user besides the
                     // default user is asking to initialize a process it runs
                     // in...  well, no, it doesn't actually run in this process,
@@ -6165,7 +6187,7 @@
             }
 
             // First check if this content provider has been published...
-            int userId = UserId.getUserId(r != null ? r.uid : Binder.getCallingUid());
+            int userId = UserHandle.getUserId(r != null ? r.uid : Binder.getCallingUid());
             cpr = mProviderMap.getProviderByName(name, userId);
             boolean providerRunning = cpr != null;
             if (providerRunning) {
@@ -6713,7 +6735,7 @@
         BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
         int uid = info.uid;
         if (isolated) {
-            int userId = UserId.getUserId(uid);
+            int userId = UserHandle.getUserId(uid);
             int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1;
             uid = 0;
             while (true) {
@@ -6721,7 +6743,7 @@
                         || mNextIsolatedProcessUid > Process.LAST_ISOLATED_UID) {
                     mNextIsolatedProcessUid = Process.FIRST_ISOLATED_UID;
                 }
-                uid = UserId.getUid(userId, mNextIsolatedProcessUid);
+                uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
                 mNextIsolatedProcessUid++;
                 if (mIsolatedProcesses.indexOfKey(uid) < 0) {
                     // No process for this uid, use it.
@@ -6759,7 +6781,7 @@
         // This package really, really can not be stopped.
         try {
             AppGlobals.getPackageManager().setPackageStoppedState(
-                    info.packageName, false, UserId.getUserId(app.uid));
+                    info.packageName, false, UserHandle.getUserId(app.uid));
         } catch (RemoteException e) {
         } catch (IllegalArgumentException e) {
             Slog.w(TAG, "Failed trying to unstop package "
@@ -8497,7 +8519,7 @@
             IPackageManager pm = AppGlobals.getPackageManager();
             for (String pkg : extList) {
                 try {
-                    ApplicationInfo info = pm.getApplicationInfo(pkg, 0, UserId.getCallingUserId());
+                    ApplicationInfo info = pm.getApplicationInfo(pkg, 0, UserHandle.getCallingUserId());
                     if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
                         retList.add(info);
                     }
@@ -9081,7 +9103,9 @@
             pw.println("  mGoingToSleep=" + mMainStack.mGoingToSleep);
             pw.println("  mLaunchingActivity=" + mMainStack.mLaunchingActivity);
             pw.println("  mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
-            pw.println("  mNumServiceProcs=" + mNumServiceProcs
+            pw.println("  mNumNonHiddenProcs=" + mNumNonHiddenProcs
+                    + " mNumHiddenProcs=" + mNumHiddenProcs
+                    + " mNumServiceProcs=" + mNumServiceProcs
                     + " mNewNumServiceProcs=" + mNewNumServiceProcs);
         }
         
@@ -9746,6 +9770,7 @@
                 pw.print("    ");
                 pw.print("oom: max="); pw.print(r.maxAdj);
                 pw.print(" hidden="); pw.print(r.hiddenAdj);
+                pw.print(" empty="); pw.print(r.emptyAdj);
                 pw.print(" curRaw="); pw.print(r.curRawAdj);
                 pw.print(" setRaw="); pw.print(r.setRawAdj);
                 pw.print(" cur="); pw.print(r.curAdj);
@@ -10243,10 +10268,10 @@
                 cpr.launchingApp = null;
                 cpr.notifyAll();
             }
-            mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid));
+            mProviderMap.removeProviderByClass(cpr.name, UserHandle.getUserId(cpr.uid));
             String names[] = cpr.info.authority.split(";");
             for (int j = 0; j < names.length; j++) {
-                mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid));
+                mProviderMap.removeProviderByName(names[j], UserHandle.getUserId(cpr.uid));
             }
         }
 
@@ -10584,7 +10609,7 @@
     boolean isSingleton(String componentProcessName, ApplicationInfo aInfo,
             String className, int flags) {
         boolean result = false;
-        if (UserId.getAppId(aInfo.uid) >= Process.FIRST_APPLICATION_UID) {
+        if (UserHandle.getAppId(aInfo.uid) >= Process.FIRST_APPLICATION_UID) {
             if ((flags&ServiceInfo.FLAG_SINGLE_USER) != 0) {
                 if (ActivityManager.checkUidPermission(
                         android.Manifest.permission.INTERACT_ACROSS_USERS,
@@ -10689,7 +10714,7 @@
             // Backup agent is now in use, its package can't be stopped.
             try {
                 AppGlobals.getPackageManager().setPackageStoppedState(
-                        app.packageName, false, UserId.getUserId(app.uid));
+                        app.packageName, false, UserHandle.getUserId(app.uid));
             } catch (RemoteException e) {
             } catch (IllegalArgumentException e) {
                 Slog.w(TAG, "Failed trying to unstop package "
@@ -11031,7 +11056,7 @@
 
         // If the caller is trying to send this broadcast to a different
         // user, verify that is allowed.
-        if (UserId.getUserId(callingUid) != userId) {
+        if (UserHandle.getUserId(callingUid) != userId) {
             if (checkComponentPermission(
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                     callingPid, callingUid, -1, true)
@@ -11045,7 +11070,7 @@
                     String msg = "Permission Denial: " + intent.getAction()
                             + " broadcast from " + callerPackage
                             + " asks to send as user " + userId
-                            + " but is calling from user " + UserId.getUserId(callingUid)
+                            + " but is calling from user " + UserHandle.getUserId(callingUid)
                             + "; this requires "
                             + android.Manifest.permission.INTERACT_ACROSS_USERS;
                     Slog.w(TAG, msg);
@@ -11538,7 +11563,7 @@
                 throw new SecurityException(msg);
             }
 
-            int userId = UserId.getCallingUserId();
+            int userId = UserHandle.getCallingUserId();
             final long origId = Binder.clearCallingIdentity();
             // Instrumentation can kill and relaunch even persistent processes
             forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, userId);
@@ -11601,7 +11626,7 @@
 
     public void finishInstrumentation(IApplicationThread target,
             int resultCode, Bundle results) {
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
         // Refuse possible leaked file descriptors
         if (results != null && results.hasFileDescriptors()) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -11914,7 +11939,7 @@
                 } else {
                     try {
                         ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
-                                destIntent.getComponent(), 0, UserId.getCallingUserId());
+                                destIntent.getComponent(), 0, UserHandle.getCallingUserId());
                         int res = mMainStack.startActivityLocked(srec.app.thread, destIntent,
                                 null, aInfo, parent.appToken, null,
                                 0, -1, parent.launchedFromUid, 0, null, true, null);
@@ -11966,14 +11991,15 @@
     }
 
     private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,
-            ProcessRecord TOP_APP, boolean recursed, boolean doingAll) {
+            int emptyAdj, ProcessRecord TOP_APP, boolean recursed, boolean doingAll) {
         if (mAdjSeq == app.adjSeq) {
             // This adjustment has already been computed.  If we are calling
             // from the top, we may have already computed our adjustment with
             // an earlier hidden adjustment that isn't really for us... if
             // so, use the new hidden adjustment.
             if (!recursed && app.hidden) {
-                app.curAdj = app.curRawAdj = app.nonStoppingAdj = hiddenAdj;
+                app.curAdj = app.curRawAdj = app.nonStoppingAdj =
+                        app.hasActivities ? hiddenAdj : emptyAdj;
             }
             return app.curRawAdj;
         }
@@ -11981,7 +12007,7 @@
         if (app.thread == null) {
             app.adjSeq = mAdjSeq;
             app.curSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
-            return (app.curAdj=ProcessList.HIDDEN_APP_MAX_ADJ);
+            return (app.curAdj=app.curRawAdj=ProcessList.HIDDEN_APP_MAX_ADJ);
         }
 
         app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
@@ -11998,6 +12024,7 @@
             app.adjType = "fixed";
             app.adjSeq = mAdjSeq;
             app.curRawAdj = app.nonStoppingAdj = app.maxAdj;
+            app.hasActivities = false;
             app.foregroundActivities = false;
             app.keeping = true;
             app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
@@ -12008,12 +12035,15 @@
             app.systemNoUi = true;
             if (app == TOP_APP) {
                 app.systemNoUi = false;
+                app.hasActivities = true;
             } else if (activitiesSize > 0) {
                 for (int j = 0; j < activitiesSize; j++) {
                     final ActivityRecord r = app.activities.get(j);
                     if (r.visible) {
                         app.systemNoUi = false;
-                        break;
+                    }
+                    if (r.app == app) {
+                        app.hasActivities = true;
                     }
                 }
             }
@@ -12022,6 +12052,7 @@
 
         app.keeping = false;
         app.systemNoUi = false;
+        app.hasActivities = false;
 
         // Determine the importance of the process, starting with most
         // important to least, and assign an appropriate OOM adjustment.
@@ -12037,6 +12068,7 @@
             app.adjType = "top-activity";
             foregroundActivities = true;
             interesting = true;
+            app.hasActivities = true;
         } else if (app.instrumentationClass != null) {
             // Don't want to kill running instrumentation.
             adj = ProcessList.FOREGROUND_APP_ADJ;
@@ -12058,21 +12090,13 @@
             adj = ProcessList.FOREGROUND_APP_ADJ;
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             app.adjType = "exec-service";
-        } else if (activitiesSize > 0) {
-            // This app is in the background with paused activities.
-            // We inspect activities to potentially upgrade adjustment further below.
+        } else {
+            // Assume process is hidden (has activities); we will correct
+            // later if this is not the case.
             adj = hiddenAdj;
             schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
             app.hidden = true;
             app.adjType = "bg-activities";
-        } else {
-            // A very not-needed process.  If this is lower in the lru list,
-            // we will push it in to the empty bucket.
-            adj = hiddenAdj;
-            schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
-            app.hidden = true;
-            app.empty = true;
-            app.adjType = "bg-empty";
         }
 
         boolean hasStoppingActivities = false;
@@ -12089,6 +12113,7 @@
                     }
                     schedGroup = Process.THREAD_GROUP_DEFAULT;
                     app.hidden = false;
+                    app.hasActivities = true;
                     foregroundActivities = true;
                     break;
                 } else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) {
@@ -12106,9 +12131,20 @@
                     foregroundActivities = true;
                     hasStoppingActivities = true;
                 }
+                if (r.app == app) {
+                    app.hasActivities = true;
+                }
             }
         }
 
+        if (adj == hiddenAdj && !app.hasActivities) {
+            // Whoops, this process is completely empty as far as we know
+            // at this point.
+            adj = emptyAdj;
+            app.empty = true;
+            app.adjType = "bg-empty";
+        }
+
         if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
             if (app.foregroundServices) {
                 // The user is aware of this app, so make it visible.
@@ -12242,8 +12278,16 @@
                                         myHiddenAdj = ProcessList.VISIBLE_APP_ADJ;
                                     }
                                 }
-                                clientAdj = computeOomAdjLocked(
-                                    client, myHiddenAdj, TOP_APP, true, doingAll);
+                                int myEmptyAdj = emptyAdj;
+                                if (myEmptyAdj > client.emptyAdj) {
+                                    if (client.emptyAdj >= ProcessList.VISIBLE_APP_ADJ) {
+                                        myEmptyAdj = client.emptyAdj;
+                                    } else {
+                                        myEmptyAdj = ProcessList.VISIBLE_APP_ADJ;
+                                    }
+                                }
+                                clientAdj = computeOomAdjLocked(client, myHiddenAdj,
+                                        myEmptyAdj, TOP_APP, true, doingAll);
                                 String adjType = null;
                                 if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
                                     // Not doing bind OOM management, so treat
@@ -12382,8 +12426,16 @@
                             myHiddenAdj = ProcessList.FOREGROUND_APP_ADJ;
                         }
                     }
-                    int clientAdj = computeOomAdjLocked(
-                        client, myHiddenAdj, TOP_APP, true, doingAll);
+                    int myEmptyAdj = emptyAdj;
+                    if (myEmptyAdj > client.emptyAdj) {
+                        if (client.emptyAdj > ProcessList.FOREGROUND_APP_ADJ) {
+                            myEmptyAdj = client.emptyAdj;
+                        } else {
+                            myEmptyAdj = ProcessList.FOREGROUND_APP_ADJ;
+                        }
+                    }
+                    int clientAdj = computeOomAdjLocked(client, myHiddenAdj,
+                            myEmptyAdj, TOP_APP, true, doingAll);
                     if (adj > clientAdj) {
                         if (app.hasShownUi && app != mHomeProcess
                                 && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
@@ -12796,9 +12848,10 @@
         }
     }
 
-    private final boolean updateOomAdjLocked(
-            ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP, boolean doingAll) {
+    private final boolean updateOomAdjLocked(ProcessRecord app, int hiddenAdj,
+            int emptyAdj, ProcessRecord TOP_APP, boolean doingAll) {
         app.hiddenAdj = hiddenAdj;
+        app.emptyAdj = emptyAdj;
 
         if (app.thread == null) {
             return false;
@@ -12808,7 +12861,7 @@
 
         boolean success = true;
 
-        computeOomAdjLocked(app, hiddenAdj, TOP_APP, false, doingAll);
+        computeOomAdjLocked(app, hiddenAdj, emptyAdj, TOP_APP, false, doingAll);
 
         if (app.curRawAdj != app.setRawAdj) {
             if (wasKeeping && !app.keeping) {
@@ -12895,7 +12948,8 @@
 
         mAdjSeq++;
 
-        boolean success = updateOomAdjLocked(app, app.hiddenAdj, TOP_APP, false);
+        boolean success = updateOomAdjLocked(app, app.hiddenAdj, app.emptyAdj,
+                TOP_APP, false);
         final boolean nowHidden = app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ
             && app.curAdj <= ProcessList.HIDDEN_APP_MAX_ADJ;
         if (nowHidden != wasHidden) {
@@ -12923,34 +12977,53 @@
         // how many slots we have for background processes; we may want
         // to put multiple processes in a slot of there are enough of
         // them.
-        int numSlots = ProcessList.HIDDEN_APP_MAX_ADJ - ProcessList.HIDDEN_APP_MIN_ADJ + 1;
-        int factor = (mLruProcesses.size()-4)/numSlots;
-        if (factor < 1) factor = 1;
-        int step = 0;
+        int numSlots = (ProcessList.HIDDEN_APP_MAX_ADJ
+                - ProcessList.HIDDEN_APP_MIN_ADJ + 1) / 2;
+        int emptyFactor = (mLruProcesses.size()-mNumNonHiddenProcs-mNumHiddenProcs)/numSlots;
+        if (emptyFactor < 1) emptyFactor = 1;
+        int hiddenFactor = (mNumHiddenProcs > 0 ? mNumHiddenProcs : 1)/numSlots;
+        if (hiddenFactor < 1) hiddenFactor = 1;
+        int stepHidden = 0;
+        int stepEmpty = 0;
+        final int emptyProcessLimit = mProcessLimit > 1 ? mProcessLimit / 2 : mProcessLimit;
+        final int hiddenProcessLimit = mProcessLimit > 1 ? mProcessLimit / 2 : mProcessLimit;
         int numHidden = 0;
+        int numEmpty = 0;
         int numTrimming = 0;
-        
+
+        mNumNonHiddenProcs = 0;
+        mNumHiddenProcs = 0;
+
         // First update the OOM adjustment for each of the
         // application processes based on their current state.
         int i = mLruProcesses.size();
         int curHiddenAdj = ProcessList.HIDDEN_APP_MIN_ADJ;
+        int nextHiddenAdj = curHiddenAdj+1;
+        int curEmptyAdj = ProcessList.HIDDEN_APP_MIN_ADJ;
+        int nextEmptyAdj = curEmptyAdj+2;
         while (i > 0) {
             i--;
             ProcessRecord app = mLruProcesses.get(i);
             //Slog.i(TAG, "OOM " + app + ": cur hidden=" + curHiddenAdj);
-            updateOomAdjLocked(app, curHiddenAdj, TOP_APP, true);
-            if (curHiddenAdj < ProcessList.HIDDEN_APP_MAX_ADJ
-                && app.curAdj == curHiddenAdj) {
-                step++;
-                if (step >= factor) {
-                    step = 0;
-                    curHiddenAdj++;
-                }
-            }
+            updateOomAdjLocked(app, curHiddenAdj, curEmptyAdj, TOP_APP, true);
             if (!app.killedBackground) {
-                if (app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
+                if (app.curRawAdj == curHiddenAdj && app.hasActivities) {
+                    // This process was assigned as a hidden process...  step the
+                    // hidden level.
+                    mNumHiddenProcs++;
+                    if (curHiddenAdj != nextHiddenAdj) {
+                        stepHidden++;
+                        if (stepHidden >= hiddenFactor) {
+                            stepHidden = 0;
+                            curHiddenAdj = nextHiddenAdj;
+                            nextHiddenAdj += 2;
+                            if (nextHiddenAdj > ProcessList.HIDDEN_APP_MAX_ADJ) {
+                                nextHiddenAdj = ProcessList.HIDDEN_APP_MAX_ADJ;
+                            }
+                        }
+                    }
                     numHidden++;
-                    if (numHidden > mProcessLimit) {
+                    if (numHidden > hiddenProcessLimit) {
                         Slog.i(TAG, "No longer want " + app.processName
                                 + " (pid " + app.pid + "): hidden #" + numHidden);
                         EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
@@ -12958,8 +13031,37 @@
                         app.killedBackground = true;
                         Process.killProcessQuiet(app.pid);
                     }
+                } else {
+                    if (app.curRawAdj == curEmptyAdj || app.curRawAdj == curHiddenAdj) {
+                        // This process was assigned as an empty process...  step the
+                        // empty level.
+                        if (curEmptyAdj != nextEmptyAdj) {
+                            stepEmpty++;
+                            if (stepEmpty >= emptyFactor) {
+                                stepEmpty = 0;
+                                curEmptyAdj = nextEmptyAdj;
+                                nextEmptyAdj += 2;
+                                if (nextEmptyAdj > ProcessList.HIDDEN_APP_MAX_ADJ) {
+                                    nextEmptyAdj = ProcessList.HIDDEN_APP_MAX_ADJ;
+                                }
+                            }
+                        }
+                    } else if (app.curRawAdj < ProcessList.HIDDEN_APP_MIN_ADJ) {
+                        mNumNonHiddenProcs++;
+                    }
+                    if (app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
+                        numEmpty++;
+                        if (numEmpty > emptyProcessLimit) {
+                            Slog.i(TAG, "No longer want " + app.processName
+                                    + " (pid " + app.pid + "): empty #" + numEmpty);
+                            EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+                                    app.processName, app.setAdj, "too many background");
+                            app.killedBackground = true;
+                            Process.killProcessQuiet(app.pid);
+                        }
+                    }
                 }
-                if (!app.killedBackground && app.isolated && app.services.size() <= 0) {
+                if (app.isolated && app.services.size() <= 0) {
                     // If this is an isolated process, and there are no
                     // services running in it, then the process is no longer
                     // needed.  We agressively kill these because we can by
@@ -12989,18 +13091,20 @@
         // are managing to keep around is less than half the maximum we desire;
         // if we are keeping a good number around, we'll let them use whatever
         // memory they want.
-        if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/2)) {
+        if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/4)
+                && numEmpty <= (ProcessList.MAX_HIDDEN_APPS/4)) {
+            final int numHiddenAndEmpty = numHidden + numEmpty;
             final int N = mLruProcesses.size();
-            factor = numTrimming/3;
+            int factor = numTrimming/3;
             int minFactor = 2;
             if (mHomeProcess != null) minFactor++;
             if (mPreviousProcess != null) minFactor++;
             if (factor < minFactor) factor = minFactor;
-            step = 0;
+            int step = 0;
             int fgTrimLevel;
-            if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/5)) {
+            if (numHiddenAndEmpty <= (ProcessList.MAX_HIDDEN_APPS/5)) {
                 fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
-            } else if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/3)) {
+            } else if (numHiddenAndEmpty <= (ProcessList.MAX_HIDDEN_APPS/3)) {
                 fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
             } else {
                 fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;
@@ -13388,7 +13492,7 @@
 
         // Inform of user switch
         Intent addedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
-        addedIntent.putExtra(Intent.EXTRA_USERID, userId);
+        addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
         mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
 
         return true;
@@ -13405,7 +13509,7 @@
     }
 
     private void onUserRemoved(Intent intent) {
-        int extraUserId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
+        int extraUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
         if (extraUserId < 1) return;
 
         // Kill all the processes for the user
@@ -13415,7 +13519,7 @@
             for (Entry<String, SparseArray<ProcessRecord>> uidMap : map.entrySet()) {
                 SparseArray<ProcessRecord> uids = uidMap.getValue();
                 for (int i = 0; i < uids.size(); i++) {
-                    if (UserId.getUserId(uids.keyAt(i)) == extraUserId) {
+                    if (UserHandle.getUserId(uids.keyAt(i)) == extraUserId) {
                         pkgAndUids.add(new Pair<String,Integer>(uidMap.getKey(), uids.keyAt(i)));
                     }
                 }
@@ -13441,14 +13545,14 @@
     }
 
     private void checkValidCaller(int uid, int userId) {
-        if (UserId.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0) return;
+        if (UserHandle.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0) return;
 
         throw new SecurityException("Caller uid=" + uid
                 + " is not privileged to communicate with user=" + userId);
     }
 
     private int applyUserId(int uid, int userId) {
-        return UserId.getUid(userId, uid);
+        return UserHandle.getUid(userId, uid);
     }
 
     ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) {
@@ -13462,7 +13566,7 @@
 
     ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) {
         if (aInfo == null
-                || (userId < 1 && aInfo.applicationInfo.uid < UserId.PER_USER_RANGE)) {
+                || (userId < 1 && aInfo.applicationInfo.uid < UserHandle.PER_USER_RANGE)) {
             return aInfo;
         }
 
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index c40abb7..b0d480c 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -37,7 +37,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
@@ -321,7 +321,7 @@
         appToken = new Token(this);
         info = aInfo;
         launchedFromUid = _launchedFromUid;
-        userId = UserId.getUserId(aInfo.applicationInfo.uid);
+        userId = UserHandle.getUserId(aInfo.applicationInfo.uid);
         intent = _intent;
         shortComponentName = _intent.getComponent().flattenToShortString();
         resolvedType = _resolvedType;
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 196a259..1e0827f 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -55,7 +55,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
@@ -501,7 +501,7 @@
 
         TaskRecord cp = null;
 
-        final int userId = UserId.getUserId(info.applicationInfo.uid);
+        final int userId = UserHandle.getUserId(info.applicationInfo.uid);
         final int N = mHistory.size();
         for (int i=(N-1); i>=0; i--) {
             ActivityRecord r = mHistory.get(i);
@@ -545,7 +545,7 @@
         if (info.targetActivity != null) {
             cls = new ComponentName(info.packageName, info.targetActivity);
         }
-        final int userId = UserId.getUserId(info.applicationInfo.uid);
+        final int userId = UserHandle.getUserId(info.applicationInfo.uid);
 
         final int N = mHistory.size();
         for (int i=(N-1); i>=0; i--) {
@@ -2406,7 +2406,7 @@
         }
 
         if (err == ActivityManager.START_SUCCESS) {
-            final int userId = aInfo != null ? UserId.getUserId(aInfo.applicationInfo.uid) : 0;
+            final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
             Slog.i(TAG, "START {" + intent.toShortString(true, true, true, false)
                     + " u=" + userId + "} from pid " + (callerApp != null ? callerApp.pid : callingPid));
         }
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index 76ddb96..7873dd8 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -36,7 +36,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.EventLog;
 import android.util.Slog;
 
@@ -373,7 +373,7 @@
             BroadcastFilter filter, boolean ordered) {
         boolean skip = false;
         if (r.onlySendToCaller) {
-            if (!UserId.isSameApp(r.callingUid, filter.owningUid)) {
+            if (!UserHandle.isSameApp(r.callingUid, filter.owningUid)) {
                 Slog.w(TAG, "Permission Denial: broadcasting "
                         + r.intent.toString()
                         + " from " + r.callerPackage + " (pid="
@@ -668,7 +668,7 @@
 
             boolean skip = false;
             if (r.onlySendToCaller) {
-                if (!UserId.isSameApp(r.callingUid, info.activityInfo.applicationInfo.uid)) {
+                if (!UserHandle.isSameApp(r.callingUid, info.activityInfo.applicationInfo.uid)) {
                     Slog.w(TAG, "Permission Denial: broadcasting "
                             + r.intent.toString()
                             + " from " + r.callerPackage + " (pid="
@@ -766,7 +766,7 @@
                 info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
             }
             r.curReceiver = info.activityInfo;
-            if (DEBUG_MU && r.callingUid > UserId.PER_USER_RANGE) {
+            if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) {
                 Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
                         + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
                         + info.activityInfo.applicationInfo.uid);
@@ -775,7 +775,7 @@
             // Broadcast is being executed, its package can't be stopped.
             try {
                 AppGlobals.getPackageManager().setPackageStoppedState(
-                        r.curComponent.getPackageName(), false, UserId.getUserId(r.callingUid));
+                        r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
             } catch (RemoteException e) {
             } catch (IllegalArgumentException e) {
                 Slog.w(TAG, "Failed trying to unstop package "
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index ad15da1..d3b8510 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -25,7 +25,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.Slog;
 
 import java.io.PrintWriter;
@@ -259,7 +259,7 @@
                             owner.broadcastIntentInPackage(key.packageName, uid,
                                     finalIntent, resolvedType,
                                     finishedReceiver, code, null, null,
-                                requiredPermission, (finishedReceiver != null), false, UserId
+                                requiredPermission, (finishedReceiver != null), false, UserHandle
                                         .getUserId(uid));
                             sendFinish = false;
                         } catch (RuntimeException e) {
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index cba9480..d372422 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -30,7 +30,7 @@
 import android.os.IBinder;
 import android.os.Process;
 import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.PrintWriterPrinter;
 import android.util.TimeUtils;
 
@@ -61,6 +61,7 @@
     long lruWeight;             // Weight for ordering in LRU list
     int maxAdj;                 // Maximum OOM adjustment for this process
     int hiddenAdj;              // If hidden, this is the adjustment to use
+    int emptyAdj;               // If empty, this is the adjustment to use
     int curRawAdj;              // Current OOM unlimited adjustment for this process
     int setRawAdj;              // Last set OOM unlimited adjustment for this process
     int nonStoppingAdj;         // Adjustment not counting any stopping activities
@@ -73,6 +74,7 @@
     boolean serviceb;           // Process currently is on the service B list
     boolean keeping;            // Actively running code so don't kill due to that?
     boolean setIsForeground;    // Running foreground UI when last set?
+    boolean hasActivities;      // Are there any activities running in this process?
     boolean foregroundServices; // Running any services that are foreground?
     boolean foregroundActivities; // Running any activities that are foreground?
     boolean systemNoUi;         // This is a system process, but not currently showing UI.
@@ -199,6 +201,7 @@
                 pw.print(" empty="); pw.println(empty);
         pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj);
                 pw.print(" hidden="); pw.print(hiddenAdj);
+                pw.print(" empty="); pw.print(emptyAdj);
                 pw.print(" curRaw="); pw.print(curRawAdj);
                 pw.print(" setRaw="); pw.print(setRawAdj);
                 pw.print(" nonStopping="); pw.print(nonStoppingAdj);
@@ -215,7 +218,9 @@
                 pw.print(" foregroundServices="); pw.print(foregroundServices);
                 pw.print(" forcingToForeground="); pw.println(forcingToForeground);
         pw.print(prefix); pw.print("persistent="); pw.print(persistent);
-                pw.print(" removed="); pw.println(removed);
+                pw.print(" removed="); pw.print(removed);
+                pw.print(" hasActivities="); pw.print(hasActivities);
+                pw.print(" foregroundActivities="); pw.println(foregroundActivities);
         pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
                 pw.print(" lruSeq="); pw.println(lruSeq);
         if (!keeping) {
@@ -308,12 +313,12 @@
         info = _info;
         isolated = _info.uid != _uid;
         uid = _uid;
-        userId = UserId.getUserId(_uid);
+        userId = UserHandle.getUserId(_uid);
         processName = _processName;
         pkgList.add(_info.packageName);
         thread = _thread;
         maxAdj = ProcessList.HIDDEN_APP_MAX_ADJ;
-        hiddenAdj = ProcessList.HIDDEN_APP_MIN_ADJ;
+        hiddenAdj = emptyAdj = ProcessList.HIDDEN_APP_MIN_ADJ;
         curRawAdj = setRawAdj = -100;
         curAdj = setAdj = -100;
         persistent = false;
@@ -391,7 +396,7 @@
             sb.append(info.uid%Process.FIRST_APPLICATION_UID);
             if (uid != info.uid) {
                 sb.append('i');
-                sb.append(UserId.getAppId(uid) - Process.FIRST_ISOLATED_UID);
+                sb.append(UserHandle.getAppId(uid) - Process.FIRST_ISOLATED_UID);
             }
         }
     }
diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java
index f4d0f9b..ab2e428 100644
--- a/services/java/com/android/server/am/ProviderMap.java
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -20,7 +20,7 @@
 import android.os.Binder;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -98,7 +98,7 @@
         if (record.singleton) {
             mSingletonByName.put(name, record);
         } else {
-            final int userId = UserId.getUserId(record.appInfo.uid);
+            final int userId = UserHandle.getUserId(record.appInfo.uid);
             getProvidersByName(userId).put(name, record);
         }
     }
@@ -111,7 +111,7 @@
         if (record.singleton) {
             mSingletonByClass.put(name, record);
         } else {
-            final int userId = UserId.getUserId(record.appInfo.uid);
+            final int userId = UserHandle.getUserId(record.appInfo.uid);
             getProvidersByClass(userId).put(name, record);
         }
     }
@@ -198,7 +198,6 @@
 
     void dumpProvidersLocked(PrintWriter pw, boolean dumpAll) {
         if (mSingletonByClass.size() > 0) {
-            pw.println("");
             pw.println("  Published single-user content providers (by class):");
             dumpProvidersByClassLocked(pw, dumpAll, mSingletonByClass);
         }
@@ -206,10 +205,10 @@
         pw.println("");
         for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
             HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i);
+            pw.println("");
             pw.println("  Published user " + mProvidersByClassPerUser.keyAt(i)
                     + " content providers (by class):");
             dumpProvidersByClassLocked(pw, dumpAll, map);
-            pw.println(" ");
         }
 
         if (dumpAll) {
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 828eef7..41386e4 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -31,7 +31,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.Slog;
 import android.util.TimeUtils;
 
@@ -296,7 +296,7 @@
         this.restarter = restarter;
         createTime = SystemClock.elapsedRealtime();
         lastActivity = SystemClock.uptimeMillis();
-        userId = UserId.getUserId(appInfo.uid);
+        userId = UserHandle.getUserId(appInfo.uid);
     }
 
     public AppBindRecord retrieveAppBindingLocked(Intent intent,
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index 3a767c2..1bae9ca 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -19,7 +19,7 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.Slog;
 
 import java.io.PrintWriter;
@@ -101,7 +101,7 @@
         }
 
         if (info.applicationInfo != null) {
-            userId = UserId.getUserId(info.applicationInfo.uid);
+            userId = UserHandle.getUserId(info.applicationInfo.uid);
         }
     }
     
diff --git a/services/java/com/android/server/location/LocationFudger.java b/services/java/com/android/server/location/LocationFudger.java
new file mode 100644
index 0000000..57bc1c5
--- /dev/null
+++ b/services/java/com/android/server/location/LocationFudger.java
@@ -0,0 +1,310 @@
+/*
+ * 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.location;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.security.SecureRandom;
+import android.location.Location;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.Log;
+
+
+/**
+ * Contains the logic to obfuscate (fudge) locations for coarse applications.
+ *
+ * <p>The goal is just to prevent applications with only
+ * the coarse location permission from receiving a fine location.
+ */
+public class LocationFudger {
+    private static final boolean D = false;
+    private static final String TAG = "LocationFudge";
+
+    private static final String EXTRA_COARSE_LOCATION = "coarseLocation";
+
+    /**
+     * This is the main control: Best location accuracy allowed for coarse applications.
+     */
+    private static final float ACCURACY_METERS = 200.0f;
+
+    /**
+     * The distance between grids for snap-to-grid. See {@link #createCoarse}.
+     */
+    private static final double GRID_SIZE_METERS = ACCURACY_METERS;
+
+    /**
+     * Standard deviation of the (normally distributed) random offset applied
+     * to coarse locations. It does not need to be as large as
+     * {@link #COARSE_ACCURACY_METERS} because snap-to-grid is the primary obfuscation
+     * method. See further details in the implementation.
+     */
+    private static final double STANDARD_DEVIATION_METERS = GRID_SIZE_METERS / 4.0;
+
+    /**
+     * This is the fastest interval that applications can receive coarse
+     * locations.
+     */
+    public static final long FASTEST_INTERVAL_MS = 10 * 60 * 1000;  // 10 minutes
+
+    /**
+     * The duration until we change the random offset.
+     */
+    private static final long CHANGE_INTERVAL_MS = 60 * 60 * 1000;  // 1 hour
+
+    /**
+     * The percentage that we change the random offset at every interval.
+     *
+     * <p>0.0 indicates the random offset doesn't change. 1.0
+     * indicates the random offset is completely replaced every interval.
+     */
+    private static final double CHANGE_PER_INTERVAL = 0.03;  // 3% change
+
+    // Pre-calculated weights used to move the random offset.
+    //
+    // The goal is to iterate on the previous offset, but keep
+    // the resulting standard deviation the same. The variance of
+    // two gaussian distributions summed together is equal to the
+    // sum of the variance of each distribution. So some quick
+    // algebra results in the following sqrt calculation to
+    // weigh in a new offset while keeping the final standard
+    // deviation unchanged.
+    private static final double NEW_WEIGHT = CHANGE_PER_INTERVAL;
+    private static final double PREVIOUS_WEIGHT = Math.sqrt(1 - NEW_WEIGHT * NEW_WEIGHT);
+
+    /**
+     * This number actually varies because the earth is not round, but
+     * 111,000 meters is considered generally acceptable.
+     */
+    private static final int APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR = 111000;
+
+    /**
+     * Maximum latitude.
+     *
+     * <p>We pick a value 1 meter away from 90.0 degrees in order
+     * to keep cosine(MAX_LATITUDE) to a non-zero value, so that we avoid
+     * divide by zero fails.
+     */
+    private static final double MAX_LATITUDE = 90.0 -
+            (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR);
+
+    private final Object mLock = new Object();
+    private final SecureRandom mRandom = new SecureRandom();
+
+    // all fields below protected by mLock
+    private double mOffsetLatitudeMeters;
+    private double mOffsetLongitudeMeters;
+    private long mNextInterval;
+
+    public LocationFudger() {
+        mOffsetLatitudeMeters = nextOffset();
+        mOffsetLongitudeMeters = nextOffset();
+        mNextInterval = SystemClock.elapsedRealtime() + CHANGE_INTERVAL_MS;
+    }
+
+    /**
+     * Get the cached coarse location, or generate a new one and cache it.
+     */
+    public Location getOrCreate(Location location) {
+        Bundle extras = location.getExtras();
+        if (extras == null) {
+            return addCoarseLocationExtra(location);
+        }
+        Parcelable parcel = extras.getParcelable(EXTRA_COARSE_LOCATION);
+        if (parcel == null) {
+            return addCoarseLocationExtra(location);
+        }
+        if (!(parcel instanceof Location)) {
+            return addCoarseLocationExtra(location);
+        }
+        Location coarse = (Location) parcel;
+        if (coarse.getAccuracy() < ACCURACY_METERS) {
+            return addCoarseLocationExtra(location);
+        }
+        return coarse;
+    }
+
+    private Location addCoarseLocationExtra(Location location) {
+        Bundle extras = location.getExtras();
+        if (extras == null) extras = new Bundle();
+        Location coarse = createCoarse(location);
+        extras.putParcelable(EXTRA_COARSE_LOCATION, coarse);
+        location.setExtras(extras);
+        return coarse;
+    }
+
+    /**
+     * Create a coarse location.
+     *
+     * <p>Two techniques are used: random offsets and snap-to-grid.
+     *
+     * <p>First we add a random offset. This mitigates against detecting
+     * grid transitions. Without a random offset it is possible to detect
+     * a users position very accurately when they cross a grid boundary.
+     * The random offset changes very slowly over time, to mitigate against
+     * taking many location samples and averaging them out.
+     *
+     * <p>Second we snap-to-grid (quantize). This has the nice property of
+     * producing stable results, and mitigating against taking many samples
+     * to average out a random offset.
+     */
+    private Location createCoarse(Location fine) {
+        Location coarse = new Location(fine);
+
+        // clean all the optional information off the location, because
+        // this can leak detailed location information
+        coarse.removeBearing();
+        coarse.removeSpeed();
+        coarse.removeAltitude();
+        coarse.setExtras(null);
+
+        double lat = coarse.getLatitude();
+        double lon = coarse.getLongitude();
+
+        // wrap
+        lat = wrapLatitude(lat);
+        lon = wrapLongitude(lon);
+
+        // Step 1) apply a random offset
+        //
+        // The goal of the random offset is to prevent the application
+        // from determining that the device is on a grid boundary
+        // when it crosses from one grid to the next.
+        //
+        // We apply the offset even if the location already claims to be
+        // inaccurate, because it may be more accurate than claimed.
+        synchronized (mLock) {
+            updateRandomOffsetLocked();
+            // perform lon first whilst lat is still within bounds
+            lon += metersToDegreesLongitude(mOffsetLongitudeMeters, lat);
+            lat += metersToDegreesLatitude(mOffsetLatitudeMeters);
+            if (D) Log.d(TAG, String.format("applied offset of %.0f, %.0f (meters)",
+                    mOffsetLongitudeMeters, mOffsetLatitudeMeters));
+        }
+
+        // wrap
+        lat = wrapLatitude(lat);
+        lon = wrapLongitude(lon);
+
+        // Step 2) Snap-to-grid (quantize)
+        //
+        // This is the primary means of obfuscation. It gives nice consistent
+        // results and is very effective at hiding the true location
+        // (as long as you are not sitting on a grid boundary, which
+        // step 1 mitigates).
+        //
+        // Note we quantize the latitude first, since the longitude
+        // quantization depends on the latitude value and so leaks information
+        // about the latitude
+        double latGranularity = metersToDegreesLatitude(GRID_SIZE_METERS);
+        lat = Math.round(lat / latGranularity) * latGranularity;
+        double lonGranularity = metersToDegreesLongitude(GRID_SIZE_METERS, lat);
+        lon = Math.round(lon / lonGranularity) * lonGranularity;
+
+        // wrap again
+        lat = wrapLatitude(lat);
+        lon = wrapLongitude(lon);
+
+        // apply
+        coarse.setLatitude(lat);
+        coarse.setLongitude(lon);
+        coarse.setAccuracy(Math.max(ACCURACY_METERS, coarse.getAccuracy()));
+
+        if (D) Log.d(TAG, "fudged " + fine + " to " + coarse);
+        return coarse;
+    }
+
+    /**
+     * Update the random offset over time.
+     *
+     * <p>If the random offset was new for every location
+     * fix then an application can more easily average location results
+     * over time,
+     * especially when the location is near a grid boundary. On the
+     * other hand if the random offset is constant then if an application
+     * found a way to reverse engineer the offset they would be able
+     * to detect location at grid boundaries very accurately. So
+     * we choose a random offset and then very slowly move it, to
+     * make both approaches very hard.
+     *
+     * <p>The random offset does not need to be large, because snap-to-grid
+     * is the primary obfuscation mechanism. It just needs to be large
+     * enough to stop information leakage as we cross grid boundaries.
+     */
+    private void updateRandomOffsetLocked() {
+        long now = SystemClock.elapsedRealtime();
+        if (now < mNextInterval) {
+            return;
+        }
+
+        if (D) Log.d(TAG, String.format("old offset: %.0f, %.0f (meters)",
+                mOffsetLongitudeMeters, mOffsetLatitudeMeters));
+
+        // ok, need to update the random offset
+        mNextInterval = now + CHANGE_INTERVAL_MS;
+
+        mOffsetLatitudeMeters *= PREVIOUS_WEIGHT;
+        mOffsetLatitudeMeters += NEW_WEIGHT * nextOffset();
+        mOffsetLongitudeMeters *= PREVIOUS_WEIGHT;
+        mOffsetLongitudeMeters += NEW_WEIGHT * nextOffset();
+
+        if (D) Log.d(TAG, String.format("new offset: %.0f, %.0f (meters)",
+                mOffsetLongitudeMeters, mOffsetLatitudeMeters));
+    }
+
+    private double nextOffset() {
+        return mRandom.nextGaussian() * STANDARD_DEVIATION_METERS;
+    }
+
+    private static double wrapLatitude(double lat) {
+         if (lat > MAX_LATITUDE) {
+             lat = MAX_LATITUDE;
+         }
+         if (lat < -MAX_LATITUDE) {
+             lat = -MAX_LATITUDE;
+         }
+         return lat;
+    }
+
+    private static double wrapLongitude(double lon) {
+        lon %= 360.0;  // wraps into range (-360.0, +360.0)
+        if (lon >= 180.0) {
+            lon -= 360.0;
+        }
+        if (lon < -180.0) {
+            lon += 360.0;
+        }
+        return lon;
+    }
+
+    private static double metersToDegreesLatitude(double distance) {
+        return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR;
+    }
+
+    /**
+     * Requires latitude since longitudinal distances change with distance from equator.
+     */
+    private static double metersToDegreesLongitude(double distance, double lat) {
+        return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR / Math.cos(Math.toRadians(lat));
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(String.format("offset: %.0f, %.0f (meters)", mOffsetLongitudeMeters,
+                mOffsetLatitudeMeters));
+    }
+}
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 46c24b0..a7cba5a 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -112,7 +112,7 @@
 import android.os.MessageQueue.IdleHandler;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
@@ -426,7 +426,7 @@
 
             final String action = intent.getAction();
             final int uid = intent.getIntExtra(EXTRA_UID, 0);
-            final int appId = UserId.getAppId(uid);
+            final int appId = UserHandle.getAppId(uid);
             synchronized (mRulesLock) {
                 if (ACTION_PACKAGE_ADDED.equals(action)) {
                     // NOTE: PACKAGE_ADDED is currently only sent once, and is
@@ -1188,8 +1188,8 @@
                         final int uid = readIntAttribute(in, ATTR_UID);
                         final int policy = readIntAttribute(in, ATTR_POLICY);
 
-                        final int appId = UserId.getAppId(uid);
-                        if (UserId.isApp(appId)) {
+                        final int appId = UserHandle.getAppId(uid);
+                        if (UserHandle.isApp(appId)) {
                             setAppPolicyUnchecked(appId, policy, false);
                         } else {
                             Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
@@ -1198,7 +1198,7 @@
                         final int appId = readIntAttribute(in, ATTR_APP_ID);
                         final int policy = readIntAttribute(in, ATTR_POLICY);
 
-                        if (UserId.isApp(appId)) {
+                        if (UserHandle.isApp(appId)) {
                             setAppPolicyUnchecked(appId, policy, false);
                         } else {
                             Slog.w(TAG, "unable to apply policy to appId " + appId + "; ignoring");
@@ -1304,7 +1304,7 @@
     public void setAppPolicy(int appId, int policy) {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
 
-        if (!UserId.isApp(appId)) {
+        if (!UserHandle.isApp(appId)) {
             throw new IllegalArgumentException("cannot apply policy to appId " + appId);
         }
 
@@ -1698,7 +1698,7 @@
         final PackageManager pm = mContext.getPackageManager();
         final List<ApplicationInfo> apps = pm.getInstalledApplications(0);
         for (ApplicationInfo app : apps) {
-            final int appId = UserId.getAppId(app.uid);
+            final int appId = UserHandle.getAppId(app.uid);
             updateRulesForAppLocked(appId);
         }
 
@@ -1710,7 +1710,7 @@
     private void updateRulesForAppLocked(int appId) {
         UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         for (UserInfo user : um.getUsers()) {
-            final int uid = UserId.getUid(user.id, appId);
+            final int uid = UserHandle.getUid(user.id, appId);
             updateRulesForUidLocked(uid);
         }
     }
@@ -1718,7 +1718,7 @@
     private static boolean isUidValidForRules(int uid) {
         // allow rules on specific system services, and any apps
         if (uid == android.os.Process.MEDIA_UID || uid == android.os.Process.DRM_UID
-                || UserId.isApp(uid)) {
+                || UserHandle.isApp(uid)) {
             return true;
         }
 
@@ -1728,7 +1728,7 @@
     private void updateRulesForUidLocked(int uid) {
         if (!isUidValidForRules(uid)) return;
 
-        final int appId = UserId.getAppId(uid);
+        final int appId = UserHandle.getAppId(uid);
         final int appPolicy = getAppPolicy(appId);
         final boolean uidForeground = isUidForeground(uid);
 
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 4befb9e..0d6e08d 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -97,10 +97,11 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.SELinux;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings.Secure;
 import android.security.SystemKeyStore;
@@ -145,6 +146,7 @@
 import libcore.io.ErrnoException;
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
+import libcore.io.StructStat;
 
 /**
  * Keep track of all those .apks everywhere.
@@ -304,8 +306,6 @@
     File mScanningPath;
     int mLastScanError;
 
-    final int[] mOutPermissions = new int[3];
-
     // ----------------------------------------------------------------
 
     // Keys are String (package name), values are Package.  This also serves
@@ -691,15 +691,15 @@
                             }
                             sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
                                     res.pkg.applicationInfo.packageName,
-                                    extras, null, null, UserId.USER_ALL);
+                                    extras, null, null, UserHandle.USER_ALL);
                             if (update) {
                                 sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
                                         res.pkg.applicationInfo.packageName,
-                                        extras, null, null, UserId.USER_ALL);
+                                        extras, null, null, UserHandle.USER_ALL);
                                 sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
                                         null, null,
                                         res.pkg.applicationInfo.packageName, null,
-                                        UserId.USER_ALL);
+                                        UserHandle.USER_ALL);
                             }
                             if (res.removedInfo.args != null) {
                                 // Remove the replaced package's older resources safely now
@@ -1630,14 +1630,14 @@
         synchronized (mPackages) {
             PackageParser.Package p = mPackages.get(packageName);
             if(p != null) {
-                return UserId.getUid(userId, p.applicationInfo.uid);
+                return UserHandle.getUid(userId, p.applicationInfo.uid);
             }
             PackageSetting ps = mSettings.mPackages.get(packageName);
             if((ps == null) || (ps.pkg == null) || (ps.pkg.applicationInfo == null)) {
                 return -1;
             }
             p = ps.pkg;
-            return p != null ? UserId.getUid(userId, p.applicationInfo.uid) : -1;
+            return p != null ? UserHandle.getUid(userId, p.applicationInfo.uid) : -1;
         }
     }
 
@@ -1961,7 +1961,7 @@
     }
 
     private void checkValidCaller(int uid, int userId) {
-        if (UserId.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0)
+        if (UserHandle.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0)
             return;
 
         throw new SecurityException("Caller uid=" + uid
@@ -1990,7 +1990,7 @@
 
     public int checkUidPermission(String permName, int uid) {
         synchronized (mPackages) {
-            Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid));
+            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
             if (obj != null) {
                 GrantedPermissions gp = (GrantedPermissions)obj;
                 if (gp.grantedPermissions.contains(permName)) {
@@ -2024,7 +2024,7 @@
         if (permName != null) {
             BasePermission bp = findPermissionTreeLP(permName);
             if (bp != null) {
-                if (bp.uid == UserId.getAppId(Binder.getCallingUid())) {
+                if (bp.uid == UserHandle.getAppId(Binder.getCallingUid())) {
                     return bp;
                 }
                 throw new SecurityException("Calling uid "
@@ -2227,8 +2227,8 @@
 
     public int checkUidSignatures(int uid1, int uid2) {
         // Map to base uids.
-        uid1 = UserId.getAppId(uid1);
-        uid2 = UserId.getAppId(uid2);
+        uid1 = UserHandle.getAppId(uid1);
+        uid2 = UserHandle.getAppId(uid2);
         // reader
         synchronized (mPackages) {
             Signature[] s1;
@@ -2286,7 +2286,7 @@
     }
 
     public String[] getPackagesForUid(int uid) {
-        uid = UserId.getAppId(uid);
+        uid = UserHandle.getAppId(uid);
         // reader
         synchronized (mPackages) {
             Object obj = mSettings.getUserIdLPr(uid);
@@ -2311,7 +2311,7 @@
     public String getNameForUid(int uid) {
         // reader
         synchronized (mPackages) {
-            Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid));
+            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
                 return sus.name + ":" + sus.userId;
@@ -2791,7 +2791,7 @@
         final ParceledListSlice<PackageInfo> list = new ParceledListSlice<PackageInfo>();
         final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
         final String[] keys;
-        int userId = UserId.getCallingUserId();
+        int userId = UserHandle.getCallingUserId();
 
         // writer
         synchronized (mPackages) {
@@ -2890,7 +2890,7 @@
         // reader
         synchronized (mPackages) {
             final Iterator<PackageParser.Package> i = mPackages.values().iterator();
-            final int userId = UserId.getCallingUserId();
+            final int userId = UserHandle.getCallingUserId();
             while (i.hasNext()) {
                 final PackageParser.Package p = i.next();
                 if (p.applicationInfo != null
@@ -2938,7 +2938,7 @@
         synchronized (mPackages) {
             final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProviders.entrySet()
                     .iterator();
-            final int userId = UserId.getCallingUserId();
+            final int userId = UserHandle.getCallingUserId();
             while (i.hasNext()) {
                 Map.Entry<String, PackageParser.Provider> entry = i.next();
                 PackageParser.Provider p = entry.getValue();
@@ -2965,14 +2965,14 @@
         synchronized (mPackages) {
             final Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator();
             final int userId = processName != null ?
-                    UserId.getUserId(uid) : UserId.getCallingUserId();
+                    UserHandle.getUserId(uid) : UserHandle.getCallingUserId();
             while (i.hasNext()) {
                 final PackageParser.Provider p = i.next();
                 PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
                 if (p.info.authority != null
                         && (processName == null
                                 || (p.info.processName.equals(processName)
-                                        && UserId.isSameApp(p.info.applicationInfo.uid, uid)))
+                                        && UserHandle.isSameApp(p.info.applicationInfo.uid, uid)))
                         && mSettings.isEnabledLPr(p.info, flags, userId)
                         && (!mSafeMode
                                 || (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
@@ -3789,14 +3789,18 @@
             boolean uidError = false;
 
             if (dataPath.exists()) {
-                // XXX should really do this check for each user.
-                mOutPermissions[1] = 0;
-                FileUtils.getPermissions(dataPath.getPath(), mOutPermissions);
+                int currentUid = 0;
+                try {
+                    StructStat stat = Libcore.os.stat(dataPath.getPath());
+                    currentUid = stat.st_uid;
+                } catch (ErrnoException e) {
+                    Slog.e(TAG, "Couldn't stat path " + dataPath.getPath(), e);
+                }
 
                 // If we have mismatched owners for the data path, we have a problem.
-                if (mOutPermissions[1] != pkg.applicationInfo.uid) {
+                if (currentUid != pkg.applicationInfo.uid) {
                     boolean recovered = false;
-                    if (mOutPermissions[1] == 0) {
+                    if (currentUid == 0) {
                         // The directory somehow became owned by root.  Wow.
                         // This is probably because the system was stopped while
                         // installd was in the middle of messing with its libs
@@ -3825,7 +3829,7 @@
                                     ? "System package " : "Third party package ";
                             String msg = prefix + pkg.packageName
                                     + " has changed from uid: "
-                                    + mOutPermissions[1] + " to "
+                                    + currentUid + " to "
                                     + pkg.applicationInfo.uid + "; old data erased";
                             reportSettingsProblem(Log.WARN, msg);
                             recovered = true;
@@ -3857,11 +3861,11 @@
                     if (!recovered) {
                         pkg.applicationInfo.dataDir = "/mismatched_uid/settings_"
                             + pkg.applicationInfo.uid + "/fs_"
-                            + mOutPermissions[1];
+                            + currentUid;
                         pkg.applicationInfo.nativeLibraryDir = pkg.applicationInfo.dataDir;
                         String msg = "Package " + pkg.packageName
                                 + " has mismatched uid: "
-                                + mOutPermissions[1] + " on disk, "
+                                + currentUid + " on disk, "
                                 + pkg.applicationInfo.uid + " in settings";
                         // writer
                         synchronized (mPackages) {
@@ -5148,7 +5152,7 @@
         IActivityManager am = ActivityManagerNative.getDefault();
         if (am != null) {
             try {
-                int[] userIds = userId == UserId.USER_ALL
+                int[] userIds = userId == UserHandle.USER_ALL
                         ? sUserManager.getUserIds() 
                         : new int[] {userId};
                 for (int id : userIds) {
@@ -5163,7 +5167,7 @@
                     // Modify the UID when posting to other users
                     int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
                     if (uid > 0 && id > 0) {
-                        uid = UserId.getUid(id, UserId.getAppId(uid));
+                        uid = UserHandle.getUid(id, UserHandle.getAppId(uid));
                         intent.putExtra(Intent.EXTRA_UID, uid);
                     }
                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
@@ -5312,13 +5316,13 @@
                 extras.putInt(Intent.EXTRA_UID, removedUid);
                 extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false);
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
-                        extras, null, null, UserId.USER_ALL);
+                        extras, null, null, UserHandle.USER_ALL);
             }
             if (addedPackage != null) {
                 Bundle extras = new Bundle(1);
                 extras.putInt(Intent.EXTRA_UID, addedUid);
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage,
-                        extras, null, null, UserId.USER_ALL);
+                        extras, null, null, UserHandle.USER_ALL);
             }
         }
 
@@ -6484,6 +6488,10 @@
                     return false;
                 }
 
+                if (!SELinux.restorecon(newCodeFile)) {
+                    return false;
+                }
+
                 return true;
             }
         }
@@ -7465,6 +7473,9 @@
             FileUtils.setPermissions(
                     tmpPackageFile.getCanonicalPath(), FileUtils.S_IRUSR|FileUtils.S_IWUSR,
                     -1, -1);
+            if (!SELinux.restorecon(tmpPackageFile)) {
+                return null;
+            }
         } catch (IOException e) {
             Slog.e(TAG, "Trouble getting the canoncical path for a temp file.");
             return null;
@@ -7539,11 +7550,11 @@
                 extras.putBoolean(Intent.EXTRA_REPLACING, true);
 
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
-                        extras, null, null, UserId.USER_ALL);
+                        extras, null, null, UserHandle.USER_ALL);
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
-                        extras, null, null, UserId.USER_ALL);
+                        extras, null, null, UserHandle.USER_ALL);
                 sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,
-                        null, packageName, null, UserId.USER_ALL);
+                        null, packageName, null, UserHandle.USER_ALL);
             }
         }
         // Force a gc here.
@@ -7576,15 +7587,15 @@
             }
             if (removedPackage != null) {
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
-                        extras, null, null, UserId.USER_ALL);
+                        extras, null, null, UserHandle.USER_ALL);
                 if (fullRemove && !replacing) {
                     sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage,
-                            extras, null, null, UserId.USER_ALL);
+                            extras, null, null, UserHandle.USER_ALL);
                 }
             }
             if (removedUid >= 0) {
                 sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null, null,
-                        UserId.getUserId(removedUid));
+                        UserHandle.getUserId(removedUid));
             }
         }
     }
@@ -7948,7 +7959,7 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.DELETE_CACHE_FILES, null);
         // Queue up an async operation since the package deletion may take a little while.
-        final int userId = UserId.getCallingUserId();
+        final int userId = UserHandle.getCallingUserId();
         mHandler.post(new Runnable() {
             public void run() {
                 mHandler.removeCallbacks(this);
@@ -8303,7 +8314,7 @@
                         + "/" + className);
             }
             // Allow root and verify that userId is not being specified by a different user
-            if (!allowedByPermission && !UserId.isSameApp(uid, pkgSetting.appId)) {
+            if (!allowedByPermission && !UserHandle.isSameApp(uid, pkgSetting.appId)) {
                 throw new SecurityException(
                         "Permission Denial: attempt to change component state from pid="
                         + Binder.getCallingPid()
@@ -8352,7 +8363,7 @@
                 }
             }
             mSettings.writePackageRestrictionsLPr(userId);
-            packageUid = UserId.getUid(userId, pkgSetting.appId);
+            packageUid = UserHandle.getUid(userId, pkgSetting.appId);
             components = mPendingBroadcasts.get(packageName);
             final boolean newPackage = components == null;
             if (newPackage) {
@@ -8401,7 +8412,7 @@
         extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
         extras.putInt(Intent.EXTRA_UID, packageUid);
         sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED,  packageName, extras, null, null,
-                UserId.getUserId(packageUid));
+                UserHandle.getUserId(packageUid));
     }
 
     public void setPackageStoppedState(String packageName, boolean stopped, int userId) {
@@ -9027,7 +9038,7 @@
             }
             String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
                     : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
-            sendPackageBroadcast(action, null, extras, null, finishedReceiver, UserId.USER_ALL);
+            sendPackageBroadcast(action, null, extras, null, finishedReceiver, UserHandle.USER_ALL);
         }
     }
 
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index add91d3..cfc0f5c 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -49,7 +49,7 @@
 import android.os.FileUtils;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -2353,7 +2353,7 @@
 
     boolean setPackageStoppedStateLPw(String packageName, boolean stopped,
             boolean allowedByPermission, int uid, int userId) {
-        int appId = UserId.getAppId(uid);
+        int appId = UserHandle.getAppId(uid);
         final PackageSetting pkgSetting = mPackages.get(packageName);
         if (pkgSetting == null) {
             throw new IllegalArgumentException("Unknown package: " + packageName);
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index b55dd24..a828864 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -22,6 +22,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -34,7 +35,8 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.SystemClock;
-import android.os.UserId;
+import android.os.UserHandle;
+import android.util.AtomicFile;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -55,22 +57,17 @@
 
 public class UserManagerService extends IUserManager.Stub {
 
-    private static final String TAG = "UserManagerService";
+    private static final String LOG_TAG = "UserManagerService";
 
     private static final String TAG_NAME = "name";
-
     private static final String ATTR_FLAGS = "flags";
-
     private static final String ATTR_ICON_PATH = "icon";
-
     private static final String ATTR_ID = "id";
-
+    private static final String ATTR_SERIAL_NO = "serialNumber";
+    private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
     private static final String TAG_USERS = "users";
-
     private static final String TAG_USER = "user";
 
-    private static final String LOG_TAG = "UserManager";
-
     private static final String USER_INFO_DIR = "system" + File.separator + "users";
     private static final String USER_LIST_FILENAME = "userlist.xml";
     private static final String USER_PHOTO_FILENAME = "photo.png";
@@ -81,6 +78,7 @@
     private final File mUserListFile;
     private int[] mUserIds;
     private boolean mGuestEnabled;
+    private int mNextSerialNumber;
 
     private Installer mInstaller;
     private File mBaseUserPath;
@@ -125,7 +123,7 @@
 
     @Override
     public List<UserInfo> getUsers() {
-        enforceSystemOrRoot("Only the system can query users");
+        checkManageUsersPermission("query users");
         synchronized (mUsers) {
             ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
             for (int i = 0; i < mUsers.size(); i++) {
@@ -137,7 +135,7 @@
 
     @Override
     public UserInfo getUserInfo(int userId) {
-        enforceSystemOrRoot("Only the system can query user");
+        checkManageUsersPermission("query user");
         synchronized (mUsers) {
             UserInfo info = mUsers.get(userId);
             return info;
@@ -152,7 +150,7 @@
 
     @Override
     public void setUserName(int userId, String name) {
-        enforceSystemOrRoot("Only the system can rename users");
+        checkManageUsersPermission("rename users");
         synchronized (mUsers) {
             UserInfo info = mUsers.get(userId);
             if (name != null && !name.equals(info.name)) {
@@ -164,7 +162,7 @@
 
     @Override
     public ParcelFileDescriptor setUserIcon(int userId) {
-        enforceSystemOrRoot("Only the system can update users");
+        checkManageUsersPermission("update users");
         synchronized (mUsers) {
             UserInfo info = mUsers.get(userId);
             if (info == null) return null;
@@ -178,7 +176,7 @@
 
     @Override
     public void setGuestEnabled(boolean enable) {
-        enforceSystemOrRoot("Only the system can enable guest users");
+        checkManageUsersPermission("enable guest users");
         synchronized (mUsers) {
             if (mGuestEnabled != enable) {
                 mGuestEnabled = enable;
@@ -209,7 +207,7 @@
 
     @Override
     public void wipeUser(int userHandle) {
-        enforceSystemOrRoot("Only the system can wipe users");
+        checkManageUsersPermission("wipe user");
         // TODO:
     }
 
@@ -220,10 +218,13 @@
      * @param message used as message if SecurityException is thrown
      * @throws SecurityException if the caller is not system or root
      */
-    private static final void enforceSystemOrRoot(String message) {
+    private static final void checkManageUsersPermission(String message) {
         final int uid = Binder.getCallingUid();
-        if (uid != Process.SYSTEM_UID && uid != 0) {
-            throw new SecurityException(message);
+        if (uid != Process.SYSTEM_UID && uid != 0
+                && ActivityManager.checkComponentPermission(
+                        android.Manifest.permission.MANAGE_USERS,
+                        uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("You need MANAGE_USERS permission to: " + message);
         }
     }
 
@@ -243,7 +244,7 @@
             info.iconPath = file.getAbsolutePath();
             return fd;
         } catch (FileNotFoundException e) {
-            Slog.w(TAG, "Error setting photo for user ", e);
+            Slog.w(LOG_TAG, "Error setting photo for user ", e);
         }
         return null;
     }
@@ -270,8 +271,9 @@
             return;
         }
         FileInputStream fis = null;
+        AtomicFile userListFile = new AtomicFile(mUserListFile);
         try {
-            fis = new FileInputStream(mUserListFile);
+            fis = userListFile.openRead();
             XmlPullParser parser = Xml.newPullParser();
             parser.setInput(fis, null);
             int type;
@@ -286,15 +288,26 @@
                 return;
             }
 
+            mNextSerialNumber = -1;
+            if (parser.getName().equals(TAG_USERS)) {
+                String lastSerialNumber = parser.getAttributeValue(null, ATTR_NEXT_SERIAL_NO);
+                if (lastSerialNumber != null) {
+                    mNextSerialNumber = Integer.parseInt(lastSerialNumber);
+                }
+            }
+
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
                 if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
                     String id = parser.getAttributeValue(null, ATTR_ID);
                     UserInfo user = readUser(Integer.parseInt(id));
                     if (user != null) {
                         mUsers.put(user.id, user);
-                    }
-                    if (user.isGuest()) {
-                        mGuestEnabled = true;
+                        if (user.isGuest()) {
+                            mGuestEnabled = true;
+                        }
+                        if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) {
+                            mNextSerialNumber = user.id + 1;
+                        }
                     }
                 }
             }
@@ -333,9 +346,9 @@
      */
     private void writeUserLocked(UserInfo userInfo) {
         FileOutputStream fos = null;
+        AtomicFile userFile = new AtomicFile(new File(mUsersDir, userInfo.id + ".xml"));
         try {
-            final File mUserFile = new File(mUsersDir, userInfo.id + ".xml");
-            fos = new FileOutputStream(mUserFile);
+            fos = userFile.startWrite();
             final BufferedOutputStream bos = new BufferedOutputStream(fos);
 
             // XmlSerializer serializer = XmlUtils.serializerInstance();
@@ -346,6 +359,7 @@
 
             serializer.startTag(null, TAG_USER);
             serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));
+            serializer.attribute(null, ATTR_SERIAL_NO, Integer.toString(userInfo.serialNumber));
             serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags));
             if (userInfo.iconPath != null) {
                 serializer.attribute(null,  ATTR_ICON_PATH, userInfo.iconPath);
@@ -358,30 +372,26 @@
             serializer.endTag(null, TAG_USER);
 
             serializer.endDocument();
-        } catch (IOException ioe) {
+            userFile.finishWrite(fos);
+        } catch (Exception ioe) {
             Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
-        } finally {
-            if (fos != null) {
-                try {
-                    fos.close();
-                } catch (IOException ioe) {
-                }
-            }
+            userFile.failWrite(fos);
         }
     }
 
     /*
      * Writes the user list file in this format:
      *
-     * <users>
+     * <users nextSerialNumber="3">
      *   <user id="0"></user>
      *   <user id="2"></user>
      * </users>
      */
     private void writeUserListLocked() {
         FileOutputStream fos = null;
+        AtomicFile userListFile = new AtomicFile(mUserListFile);
         try {
-            fos = new FileOutputStream(mUserListFile);
+            fos = userListFile.startWrite();
             final BufferedOutputStream bos = new BufferedOutputStream(fos);
 
             // XmlSerializer serializer = XmlUtils.serializerInstance();
@@ -391,6 +401,7 @@
             serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
 
             serializer.startTag(null, TAG_USERS);
+            serializer.attribute(null, ATTR_NEXT_SERIAL_NO, Integer.toString(mNextSerialNumber));
 
             for (int i = 0; i < mUsers.size(); i++) {
                 UserInfo user = mUsers.valueAt(i);
@@ -402,27 +413,24 @@
             serializer.endTag(null, TAG_USERS);
 
             serializer.endDocument();
-        } catch (IOException ioe) {
+            userListFile.finishWrite(fos);
+        } catch (Exception e) {
+            userListFile.failWrite(fos);
             Slog.e(LOG_TAG, "Error writing user list");
-        } finally {
-            if (fos != null) {
-                try {
-                    fos.close();
-                } catch (IOException ioe) {
-                }
-            }
         }
     }
 
     private UserInfo readUser(int id) {
         int flags = 0;
+        int serialNumber = id;
         String name = null;
         String iconPath = null;
 
         FileInputStream fis = null;
         try {
-            File userFile = new File(mUsersDir, Integer.toString(id) + ".xml");
-            fis = new FileInputStream(userFile);
+            AtomicFile userFile =
+                    new AtomicFile(new File(mUsersDir, Integer.toString(id) + ".xml"));
+            fis = userFile.openRead();
             XmlPullParser parser = Xml.newPullParser();
             parser.setInput(fis, null);
             int type;
@@ -442,6 +450,10 @@
                     Slog.e(LOG_TAG, "User id does not match the file name");
                     return null;
                 }
+                String serialNumberValue = parser.getAttributeValue(null, ATTR_SERIAL_NO);
+                if (serialNumberValue != null) {
+                    serialNumber = Integer.parseInt(serialNumberValue);
+                }
                 String flagString = parser.getAttributeValue(null, ATTR_FLAGS);
                 flags = Integer.parseInt(flagString);
                 iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH);
@@ -458,6 +470,7 @@
             }
 
             UserInfo userInfo = new UserInfo(id, name, iconPath, flags);
+            userInfo.serialNumber = serialNumber;
             return userInfo;
 
         } catch (IOException ioe) {
@@ -475,7 +488,7 @@
 
     @Override
     public UserInfo createUser(String name, int flags) {
-        enforceSystemOrRoot("Only the system can create users");
+        checkManageUsersPermission("Only the system can create users");
         int userId = getNextAvailableId();
         UserInfo userInfo = new UserInfo(userId, name, null, flags);
         File userPath = new File(mBaseUserPath, Integer.toString(userId));
@@ -483,6 +496,7 @@
             return null;
         }
         synchronized (mUsers) {
+            userInfo.serialNumber = mNextSerialNumber++;
             mUsers.put(userId, userInfo);
             writeUserListLocked();
             writeUserLocked(userInfo);
@@ -490,8 +504,8 @@
         }
         if (userInfo != null) {
             Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
-            addedIntent.putExtra(Intent.EXTRA_USERID, userInfo.id);
-            mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
+            addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
+            mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
         }
         return userInfo;
     }
@@ -502,9 +516,34 @@
      * @param id the user's id
      */
     public boolean removeUser(int userHandle) {
-        enforceSystemOrRoot("Only the system can remove users");
+        checkManageUsersPermission("Only the system can remove users");
+        boolean result;
         synchronized (mUsers) {
-            return removeUserLocked(userHandle);
+            result = removeUserLocked(userHandle);
+        }
+        // Let other services shutdown any activity
+        Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
+        addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
+        mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
+        return result;
+    }
+
+    @Override
+    public int getUserSerialNumber(int userHandle) {
+        synchronized (mUsers) {
+            if (!exists(userHandle)) return -1;
+            return getUserInfo(userHandle).serialNumber;
+        }
+    }
+
+    @Override
+    public int getUserHandle(int userSerialNumber) {
+        synchronized (mUsers) {
+            for (int userId : mUserIds) {
+                if (getUserInfo(userId).serialNumber == userSerialNumber) return userId;
+            }
+            // Not found
+            return -1;
         }
     }
 
@@ -519,17 +558,12 @@
         // Remove this user from the list
         mUsers.remove(userHandle);
         // Remove user file
-        File userFile = new File(mUsersDir, userHandle + ".xml");
+        AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml"));
         userFile.delete();
         // Update the user list
         writeUserListLocked();
         updateUserIdsLocked();
 
-        // Let other services shutdown any activity
-        Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
-        addedIntent.putExtra(Intent.EXTRA_USERID, userHandle);
-        mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
-
         removePackageFolders(userHandle);
         return true;
     }
@@ -539,7 +573,7 @@
             // Don't do it for the primary user, it will become recursive.
             if (userId == 0)
                 continue;
-            mInstaller.createUserData(packageName, UserId.getUid(userId, uid),
+            mInstaller.createUserData(packageName, UserHandle.getUid(userId, uid),
                     userId);
         }
     }
diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java
new file mode 100644
index 0000000..f3183f8
--- /dev/null
+++ b/services/java/com/android/server/power/DisplayPowerController.java
@@ -0,0 +1,1032 @@
+/*
+ * 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;
+
+import com.android.server.LightsService;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.SystemSensorManager;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.Spline;
+import android.util.TimeUtils;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+
+/**
+ * Controls the power state of the display.
+ *
+ * Handles the proximity sensor, light sensor, and animations between states
+ * including the screen off animation.
+ *
+ * This component acts independently of the rest of the power manager service.
+ * In particular, it does not share any state and it only communicates
+ * via asynchronous callbacks to inform the power manager that something has
+ * changed.
+ *
+ * Everything this class does internally is serialized on its handler although
+ * it may be accessed by other threads from the outside.
+ *
+ * Note that the power manager service guarantees that it will hold a suspend
+ * blocker as long as the display is not ready.  So most of the work done here
+ * does not need to worry about holding a suspend blocker unless it happens
+ * independently of the display ready signal.
+ *
+ * For debugging, you can make the electron beam and brightness animations run
+ * slower by changing the "animator duration scale" option in Development Settings.
+ */
+final class DisplayPowerController {
+    private static final String TAG = "DisplayPowerController";
+
+    private static boolean DEBUG = false;
+    private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false;
+    private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
+
+    // If true, uses the electron beam on animation.
+    // We might want to turn this off if we cannot get a guarantee that the screen
+    // actually turns on and starts showing new content after the call to set the
+    // screen state returns.
+    private static final boolean USE_ELECTRON_BEAM_ON_ANIMATION = true;
+
+    private static final int ELECTRON_BEAM_ON_ANIMATION_DURATION_MILLIS = 300;
+    private static final int ELECTRON_BEAM_OFF_ANIMATION_DURATION_MILLIS = 600;
+
+    private static final int MSG_UPDATE_POWER_STATE = 1;
+    private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2;
+    private static final int MSG_LIGHT_SENSOR_DEBOUNCED = 3;
+
+    private static final int PROXIMITY_UNKNOWN = -1;
+    private static final int PROXIMITY_NEGATIVE = 0;
+    private static final int PROXIMITY_POSITIVE = 1;
+
+    // Proximity sensor debounce delay in milliseconds.
+    private static final int PROXIMITY_SENSOR_DEBOUNCE_DELAY = 250;
+
+    // Trigger proximity if distance is less than 5 cm.
+    private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
+
+    // Light sensor event rate in microseconds.
+    private static final int LIGHT_SENSOR_RATE = 1000000;
+
+    // Brightness animation ramp rate in brightness units per second.
+    private static final int BRIGHTNESS_RAMP_RATE_FAST = 200;
+    private static final int BRIGHTNESS_RAMP_RATE_SLOW = 50;
+
+    // Filter time constant in milliseconds for computing a moving
+    // average of light samples.  Different constants are used
+    // to calculate the average light level when adapting to brighter or
+    // dimmer environments.
+    // This parameter only controls the filtering of light samples.
+    private static final long BRIGHTENING_LIGHT_TIME_CONSTANT = 500;
+    private static final long DIMMING_LIGHT_TIME_CONSTANT = 2000;
+
+    // Stability requirements in milliseconds for accepting a new brightness
+    // level.  This is used for debouncing the light sensor.  Different constants
+    // are used to debounce the light sensor when adapting to brighter or dimmer
+    // environments.
+    // This parameter controls how quickly brightness changes occur in response to
+    // an observed change in light level.
+    private static final long BRIGHTENING_LIGHT_DEBOUNCE = 2500;
+    private static final long DIMMING_LIGHT_DEBOUNCE = 10000;
+
+    private final Object mLock = new Object();
+
+    // Notifier for sending asynchronous notifications.
+    private final Notifier mNotifier;
+
+    // A suspend blocker.
+    private final SuspendBlocker mSuspendBlocker;
+
+    // Our handler.
+    private final DisplayControllerHandler mHandler;
+
+    // Asynchronous callbacks into the power manager service.
+    // Only invoked from the handler thread while no locks are held.
+    private final Callbacks mCallbacks;
+    private Handler mCallbackHandler;
+
+    // The lights service.
+    private final LightsService mLights;
+
+    // The sensor manager.
+    private final SensorManager mSensorManager;
+
+    // The proximity sensor, or null if not available or needed.
+    private Sensor mProximitySensor;
+
+    // The light sensor, or null if not available or needed.
+    private Sensor mLightSensor;
+
+    // The dim screen brightness.
+    private final int mScreenBrightnessDimConfig;
+
+    // Auto-brightness.
+    private boolean mUseSoftwareAutoBrightnessConfig;
+    private Spline mScreenAutoBrightnessSpline;
+
+    // Amount of time to delay auto-brightness after screen on while waiting for
+    // the light sensor to warm-up in milliseconds.
+    // May be 0 if no warm-up is required.
+    private int mLightSensorWarmUpTimeConfig;
+
+    // The pending power request.
+    // Initially null until the first call to requestPowerState.
+    // Guarded by mLock.
+    private DisplayPowerRequest mPendingRequestLocked;
+
+    // True if a request has been made to wait for the proximity sensor to go negative.
+    // Guarded by mLock.
+    private boolean mPendingWaitForNegativeProximityLocked;
+
+    // True if the pending power request or wait for negative proximity flag
+    // has been changed since the last update occurred.
+    // Guarded by mLock.
+    private boolean mPendingRequestChangedLocked;
+
+    // Set to true when the important parts of the pending power request have been applied.
+    // The important parts are mainly the screen state.  Brightness changes may occur
+    // concurrently.
+    // Guarded by mLock.
+    private boolean mDisplayReadyLocked;
+
+    // Set to true if a power state update is required.
+    // Guarded by mLock.
+    private boolean mPendingUpdatePowerStateLocked;
+
+    /* The following state must only be accessed by the handler thread. */
+
+    // The currently requested power state.
+    // The power controller will progressively update its internal state to match
+    // the requested power state.  Initially null until the first update.
+    private DisplayPowerRequest mPowerRequest;
+
+    // The current power state.
+    // Must only be accessed on the handler thread.
+    private DisplayPowerState mPowerState;
+
+    // True if the device should wait for negative proximity sensor before
+    // waking up the screen.  This is set to false as soon as a negative
+    // proximity sensor measurement is observed or when the device is forced to
+    // go to sleep by the user.  While true, the screen remains off.
+    private boolean mWaitingForNegativeProximity;
+
+    // The actual proximity sensor threshold value.
+    private float mProximityThreshold;
+
+    // Set to true if the proximity sensor listener has been registered
+    // with the sensor manager.
+    private boolean mProximitySensorEnabled;
+
+    // The debounced proximity sensor state.
+    private int mProximity = PROXIMITY_UNKNOWN;
+
+    // The raw non-debounced proximity sensor state.
+    private int mPendingProximity = PROXIMITY_UNKNOWN;
+    private long mPendingProximityDebounceTime;
+
+    // True if the screen was turned off because of the proximity sensor.
+    // When the screen turns on again, we report user activity to the power manager.
+    private boolean mScreenOffBecauseOfProximity;
+
+    // Set to true if the light sensor is enabled.
+    private boolean mLightSensorEnabled;
+
+    // The time when the light sensor was enabled.
+    private long mLightSensorEnableTime;
+
+    // The currently accepted average light sensor value.
+    private float mLightMeasurement;
+
+    // True if the light sensor measurement is valid.
+    private boolean mLightMeasurementValid;
+
+    // The number of light sensor samples that have been collected since the
+    // last time a light sensor reading was accepted.
+    private int mRecentLightSamples;
+
+    // The moving average of recent light sensor values.
+    private float mRecentLightAverage;
+
+    // True if recent light samples are getting brighter than the previous
+    // stable light measurement.
+    private boolean mRecentLightBrightening;
+
+    // The time constant to use for filtering based on whether the
+    // light appears to be brightening or dimming.
+    private long mRecentLightTimeConstant;
+
+    // The most recent light sample.
+    private float mLastLightSample;
+
+    // The time of the most light recent sample.
+    private long mLastLightSampleTime;
+
+    // The time when we accumulated the first recent light sample into mRecentLightSamples.
+    private long mFirstRecentLightSampleTime;
+
+    // The upcoming debounce light sensor time.
+    // This is only valid when mLightMeasurementValue && mRecentLightSamples >= 1.
+    private long mPendingLightSensorDebounceTime;
+
+    // The screen brightness level that has been chosen by the auto-brightness
+    // algorithm.  The actual brightness should ramp towards this value.
+    // We preserve this value even when we stop using the light sensor so
+    // that we can quickly revert to the previous auto-brightness level
+    // while the light sensor warms up.
+    // Use -1 if there is no current auto-brightness value available.
+    private int mScreenAutoBrightness = -1;
+
+    // True if the screen auto-brightness value is actually being used to
+    // set the display brightness.
+    private boolean mUsingScreenAutoBrightness;
+
+    // Animators.
+    private ObjectAnimator mElectronBeamOnAnimator;
+    private ObjectAnimator mElectronBeamOffAnimator;
+    private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
+
+    /**
+     * Creates the display power controller.
+     */
+    public DisplayPowerController(Looper looper, Context context, Notifier notifier,
+            LightsService lights, SuspendBlocker suspendBlocker,
+            Callbacks callbacks, Handler callbackHandler) {
+        mHandler = new DisplayControllerHandler(looper);
+        mNotifier = notifier;
+        mSuspendBlocker = suspendBlocker;
+        mCallbacks = callbacks;
+        mCallbackHandler = callbackHandler;
+
+        mLights = lights;
+        mSensorManager = new SystemSensorManager(mHandler.getLooper());
+
+        final Resources resources = context.getResources();
+        mScreenBrightnessDimConfig = resources.getInteger(
+                com.android.internal.R.integer.config_screenBrightnessDim);
+        mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
+                com.android.internal.R.bool.config_automatic_brightness_available);
+        if (mUseSoftwareAutoBrightnessConfig) {
+            int[] lux = resources.getIntArray(
+                    com.android.internal.R.array.config_autoBrightnessLevels);
+            int[] screenBrightness = resources.getIntArray(
+                    com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
+
+            mScreenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness);
+            if (mScreenAutoBrightnessSpline == null) {
+                Slog.e(TAG, "Error in config.xml.  config_autoBrightnessLcdBacklightValues "
+                        + "(size " + screenBrightness.length + ") "
+                        + "must be monotic and have exactly one more entry than "
+                        + "config_autoBrightnessLevels (size " + lux.length + ") "
+                        + "which must be strictly increasing.  "
+                        + "Auto-brightness will be disabled.");
+                mUseSoftwareAutoBrightnessConfig = false;
+            }
+
+            mLightSensorWarmUpTimeConfig = resources.getInteger(
+                    com.android.internal.R.integer.config_lightSensorWarmupTime);
+        }
+
+        if (!DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) {
+            mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+            if (mProximitySensor != null) {
+                mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(),
+                        TYPICAL_PROXIMITY_THRESHOLD);
+            }
+        }
+
+        if (mUseSoftwareAutoBrightnessConfig
+                && !DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
+            mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
+        }
+    }
+
+    private static Spline createAutoBrightnessSpline(int[] lux, int[] brightness) {
+        try {
+            final int n = brightness.length;
+            float[] x = new float[n];
+            float[] y = new float[n];
+            y[0] = brightness[0];
+            for (int i = 1; i < n; i++) {
+                x[i] = lux[i - 1];
+                y[i] = brightness[i];
+            }
+
+            Spline spline = Spline.createMonotoneCubicSpline(x, y);
+            if (false) {
+                Slog.d(TAG, "Auto-brightness spline: " + spline);
+                for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {
+                    Slog.d(TAG, String.format("  %7.1f: %7.1f", v, spline.interpolate(v)));
+                }
+            }
+            return spline;
+        } catch (IllegalArgumentException ex) {
+            Slog.e(TAG, "Could not create auto-brightness spline.", ex);
+            return null;
+        }
+    }
+
+    /**
+     * Returns true if the proximity sensor screen-off function is available.
+     */
+    public boolean isProximitySensorAvailable() {
+        return mProximitySensor != null;
+    }
+
+    /**
+     * Requests a new power state.
+     * The controller makes a copy of the provided object and then
+     * begins adjusting the power state to match what was requested.
+     *
+     * @param request The requested power state.
+     * @param waitForNegativeProximity If true, issues a request to wait for
+     * negative proximity before turning the screen back on, assuming the screen
+     * was turned off by the proximity sensor.
+     * @return True if display is ready, false if there are important changes that must
+     * be made asynchronously (such as turning the screen on), in which case the caller
+     * should grab a wake lock, watch for {@link Callbacks#onStateChanged()} then try
+     * the request again later until the state converges.
+     */
+    public boolean requestPowerState(DisplayPowerRequest request,
+            boolean waitForNegativeProximity) {
+        if (DEBUG) {
+            Slog.d(TAG, "requestPowerState: "
+                    + request + ", waitForNegativeProximity=" + waitForNegativeProximity);
+        }
+
+        synchronized (mLock) {
+            boolean changed = false;
+
+            if (waitForNegativeProximity
+                    && !mPendingWaitForNegativeProximityLocked) {
+                mPendingWaitForNegativeProximityLocked = true;
+                changed = true;
+            }
+
+            if (mPendingRequestLocked == null) {
+                mPendingRequestLocked = new DisplayPowerRequest(request);
+                changed = true;
+            } else if (!mPendingRequestLocked.equals(request)) {
+                mPendingRequestLocked.copyFrom(request);
+                changed = true;
+            }
+
+            if (changed) {
+                mDisplayReadyLocked = false;
+            }
+
+            if (changed && !mPendingRequestChangedLocked) {
+                mPendingRequestChangedLocked = true;
+                sendUpdatePowerStateLocked();
+            }
+
+            return mDisplayReadyLocked;
+        }
+    }
+
+    private void sendUpdatePowerState() {
+        synchronized (mLock) {
+            sendUpdatePowerStateLocked();
+        }
+    }
+
+    private void sendUpdatePowerStateLocked() {
+        if (!mPendingUpdatePowerStateLocked) {
+            mPendingUpdatePowerStateLocked = true;
+            Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
+            msg.setAsynchronous(true);
+            mHandler.sendMessage(msg);
+        }
+    }
+
+    private void initialize() {
+        final Executor executor = AsyncTask.THREAD_POOL_EXECUTOR;
+        mPowerState = new DisplayPowerState(new ElectronBeam(),
+                new PhotonicModulator(executor,
+                        mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT),
+                        mSuspendBlocker));
+
+        mElectronBeamOnAnimator = ObjectAnimator.ofFloat(
+                mPowerState, DisplayPowerState.ELECTRON_BEAM_LEVEL, 0.0f, 1.0f);
+        mElectronBeamOnAnimator.setDuration(ELECTRON_BEAM_ON_ANIMATION_DURATION_MILLIS);
+        mElectronBeamOnAnimator.addListener(mAnimatorListener);
+
+        mElectronBeamOffAnimator = ObjectAnimator.ofFloat(
+                mPowerState, DisplayPowerState.ELECTRON_BEAM_LEVEL, 1.0f, 0.0f);
+        mElectronBeamOffAnimator.setDuration(ELECTRON_BEAM_OFF_ANIMATION_DURATION_MILLIS);
+        mElectronBeamOffAnimator.addListener(mAnimatorListener);
+
+        mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>(
+                mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS);
+    }
+
+    private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
+        @Override
+        public void onAnimationStart(Animator animation) {
+        }
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            sendUpdatePowerState();
+        }
+        @Override
+        public void onAnimationRepeat(Animator animation) {
+        }
+        @Override
+        public void onAnimationCancel(Animator animation) {
+        }
+    };
+
+    private void updatePowerState() {
+        // Update the power state request.
+        final boolean mustNotify;
+        boolean mustInitialize = false;
+        synchronized (mLock) {
+            mPendingUpdatePowerStateLocked = false;
+            if (mPendingRequestLocked == null) {
+                return; // wait until first actual power request
+            }
+
+            if (mPowerRequest == null) {
+                mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked);
+                mWaitingForNegativeProximity = mPendingWaitForNegativeProximityLocked;
+                mPendingRequestChangedLocked = false;
+                mustInitialize = true;
+            } else if (mPendingRequestChangedLocked) {
+                mPowerRequest.copyFrom(mPendingRequestLocked);
+                mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
+                mPendingRequestChangedLocked = false;
+                mDisplayReadyLocked = false;
+            }
+
+            mustNotify = !mDisplayReadyLocked;
+        }
+
+        // Initialize things the first time the power state is changed.
+        if (mustInitialize) {
+            initialize();
+        }
+
+        // Clear a request to wait for negative proximity if needed.
+        if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_OFF
+                || mProximity == PROXIMITY_NEGATIVE
+                || mProximitySensor == null) {
+            mWaitingForNegativeProximity = false;
+        }
+
+        // Turn on the proximity sensor if needed.
+        if (mProximitySensor != null) {
+            setProximitySensorEnabled(mPowerRequest.useProximitySensor
+                    || mWaitingForNegativeProximity);
+            if (mProximitySensorEnabled && mProximity == PROXIMITY_POSITIVE) {
+                mScreenOffBecauseOfProximity = true;
+                setScreenOn(false);
+            } else if (mScreenOffBecauseOfProximity) {
+                mScreenOffBecauseOfProximity = false;
+                sendOnProximityNegative();
+            }
+        }
+
+        // Turn on the light sensor if needed.
+        if (mLightSensor != null) {
+            setLightSensorEnabled(mPowerRequest.useAutoBrightness
+                    && wantScreenOn(mPowerRequest.screenState));
+        }
+
+        // Set the screen brightness.
+        if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM) {
+            // Screen is dimmed.  Overrides everything else.
+            animateScreenBrightness(mScreenBrightnessDimConfig, BRIGHTNESS_RAMP_RATE_FAST);
+            mUsingScreenAutoBrightness = false;
+        } else if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_BRIGHT) {
+            if (mScreenAutoBrightness >= 0 && mLightSensorEnabled) {
+                // Use current auto-brightness value.
+                animateScreenBrightness(
+                        Math.max(mScreenAutoBrightness, mScreenBrightnessDimConfig),
+                        mUsingScreenAutoBrightness ? BRIGHTNESS_RAMP_RATE_SLOW :
+                                BRIGHTNESS_RAMP_RATE_FAST);
+                mUsingScreenAutoBrightness = true;
+            } else {
+                // Light sensor is disabled or not ready yet.
+                // Use the current brightness setting from the request, which is expected
+                // provide a nominal default value for the case where auto-brightness
+                // is not ready yet.
+                animateScreenBrightness(
+                        Math.max(mPowerRequest.screenBrightness, mScreenBrightnessDimConfig),
+                        BRIGHTNESS_RAMP_RATE_FAST);
+                mUsingScreenAutoBrightness = false;
+            }
+        } else {
+            // Screen is off.  Don't bother changing the brightness.
+            mUsingScreenAutoBrightness = false;
+        }
+
+        // Animate the screen on or off.
+        if (!mScreenOffBecauseOfProximity) {
+            if (wantScreenOn(mPowerRequest.screenState)) {
+                // Want screen on.
+                // Wait for previous off animation to complete beforehand.
+                // It is relatively short but if we cancel it and switch to the
+                // on animation immediately then the results are pretty ugly.
+                if (!mElectronBeamOffAnimator.isStarted()) {
+                    setScreenOn(true);
+                    if (USE_ELECTRON_BEAM_ON_ANIMATION) {
+                        if (!mElectronBeamOnAnimator.isStarted()) {
+                            if (mPowerState.getElectronBeamLevel() == 1.0f) {
+                                mPowerState.dismissElectronBeam();
+                            } else if (mPowerState.prepareElectronBeam(true)) {
+                                mElectronBeamOnAnimator.start();
+                            } else {
+                                mElectronBeamOnAnimator.end();
+                            }
+                        }
+                    } else {
+                        mPowerState.setElectronBeamLevel(1.0f);
+                        mPowerState.dismissElectronBeam();
+                    }
+                }
+            } else {
+                // Want screen off.
+                // Wait for previous on animation to complete beforehand.
+                if (!mElectronBeamOnAnimator.isStarted()) {
+                    if (!mElectronBeamOffAnimator.isStarted()) {
+                        if (mPowerState.getElectronBeamLevel() == 0.0f) {
+                            setScreenOn(false);
+                        } else if (mPowerState.prepareElectronBeam(false)
+                                && mPowerState.isScreenOn()) {
+                            mElectronBeamOffAnimator.start();
+                        } else {
+                            mElectronBeamOffAnimator.end();
+                        }
+                    }
+                }
+            }
+        }
+
+        // Report whether the display is ready for use.
+        // We mostly care about the screen state here, ignoring brightness changes
+        // which will be handled asynchronously.
+        if (mustNotify
+                && !mElectronBeamOnAnimator.isStarted()
+                && !mElectronBeamOffAnimator.isStarted()
+                && mPowerState.waitUntilClean(mCleanListener)) {
+            synchronized (mLock) {
+                if (!mPendingRequestChangedLocked) {
+                    mDisplayReadyLocked = true;
+                }
+            }
+            sendOnStateChanged();
+        }
+    }
+
+    private void setScreenOn(boolean on) {
+        if (!mPowerState.isScreenOn() == on) {
+            mPowerState.setScreenOn(on);
+            if (on) {
+                mNotifier.onScreenOn();
+            } else {
+                mNotifier.onScreenOff();
+            }
+        }
+    }
+
+    private void animateScreenBrightness(int target, int rate) {
+        if (mScreenBrightnessRampAnimator.animateTo(target, rate)) {
+            mNotifier.onScreenBrightness(target);
+        }
+    }
+
+    private final Runnable mCleanListener = new Runnable() {
+        @Override
+        public void run() {
+            sendUpdatePowerState();
+        }
+    };
+
+    private void setProximitySensorEnabled(boolean enable) {
+        if (enable) {
+            if (!mProximitySensorEnabled) {
+                mProximitySensorEnabled = true;
+                mPendingProximity = PROXIMITY_UNKNOWN;
+                mSensorManager.registerListener(mProximitySensorListener, mProximitySensor,
+                        SensorManager.SENSOR_DELAY_NORMAL, mHandler);
+            }
+        } else {
+            if (mProximitySensorEnabled) {
+                mProximitySensorEnabled = false;
+                mProximity = PROXIMITY_UNKNOWN;
+                mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
+                mSensorManager.unregisterListener(mProximitySensorListener);
+            }
+        }
+    }
+
+    private void handleProximitySensorEvent(long time, boolean positive) {
+        if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) {
+            return; // no change
+        }
+        if (mPendingProximity == PROXIMITY_POSITIVE && positive) {
+            return; // no change
+        }
+
+        // Only accept a proximity sensor reading if it remains
+        // stable for the entire debounce delay.
+        mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
+        mPendingProximity = positive ? PROXIMITY_POSITIVE : PROXIMITY_NEGATIVE;
+        mPendingProximityDebounceTime = time + PROXIMITY_SENSOR_DEBOUNCE_DELAY;
+        debounceProximitySensor();
+    }
+
+    private void debounceProximitySensor() {
+        if (mPendingProximity != PROXIMITY_UNKNOWN) {
+            final long now = SystemClock.uptimeMillis();
+            if (mPendingProximityDebounceTime <= now) {
+                mProximity = mPendingProximity;
+                sendUpdatePowerState();
+            } else {
+                Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED);
+                msg.setAsynchronous(true);
+                mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime);
+            }
+        }
+    }
+
+    private void setLightSensorEnabled(boolean enable) {
+        if (enable) {
+            if (!mLightSensorEnabled) {
+                mLightSensorEnabled = true;
+                mLightSensorEnableTime = SystemClock.uptimeMillis();
+                mSensorManager.registerListener(mLightSensorListener, mLightSensor,
+                        LIGHT_SENSOR_RATE, mHandler);
+            }
+        } else {
+            if (mLightSensorEnabled) {
+                mLightSensorEnabled = false;
+                mLightMeasurementValid = false;
+                updateAutoBrightness(false);
+                mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED);
+                mSensorManager.unregisterListener(mLightSensorListener);
+            }
+        }
+    }
+
+    private void handleLightSensorEvent(long time, float lux) {
+        // Take the first few readings during the warm-up period and apply them
+        // immediately without debouncing.
+        if (!mLightMeasurementValid
+                || (time - mLightSensorEnableTime) < mLightSensorWarmUpTimeConfig) {
+            mLightMeasurement = lux;
+            mLightMeasurementValid = true;
+            mRecentLightSamples = 0;
+            updateAutoBrightness(true);
+        }
+
+        // Update our moving average.
+        if (lux != mLightMeasurement && (mRecentLightSamples == 0
+                || (lux < mLightMeasurement && mRecentLightBrightening)
+                || (lux > mLightMeasurement && !mRecentLightBrightening))) {
+            // If the newest light sample doesn't seem to be going in the
+            // same general direction as recent samples, then start over.
+            setRecentLight(time, lux, lux > mLightMeasurement);
+        } else if (mRecentLightSamples >= 1) {
+            // Add the newest light sample to the moving average.
+            accumulateRecentLight(time, lux);
+        }
+        if (DEBUG) {
+            Slog.d(TAG, "handleLightSensorEvent: lux=" + lux
+                    + ", mLightMeasurementValid=" + mLightMeasurementValid
+                    + ", mLightMeasurement=" + mLightMeasurement
+                    + ", mRecentLightSamples=" + mRecentLightSamples
+                    + ", mRecentLightAverage=" + mRecentLightAverage
+                    + ", mRecentLightBrightening=" + mRecentLightBrightening
+                    + ", mRecentLightTimeConstant=" + mRecentLightTimeConstant
+                    + ", mFirstRecentLightSampleTime="
+                            + TimeUtils.formatUptime(mFirstRecentLightSampleTime)
+                    + ", mPendingLightSensorDebounceTime="
+                            + TimeUtils.formatUptime(mPendingLightSensorDebounceTime));
+        }
+
+        // Debounce.
+        mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED);
+        debounceLightSensor();
+    }
+
+    private void setRecentLight(long time, float lux, boolean brightening) {
+        mRecentLightBrightening = brightening;
+        mRecentLightTimeConstant = brightening ?
+                BRIGHTENING_LIGHT_TIME_CONSTANT : DIMMING_LIGHT_TIME_CONSTANT;
+        mRecentLightSamples = 1;
+        mRecentLightAverage = lux;
+        mLastLightSample = lux;
+        mLastLightSampleTime = time;
+        mFirstRecentLightSampleTime = time;
+        mPendingLightSensorDebounceTime = time + (brightening ?
+                BRIGHTENING_LIGHT_DEBOUNCE : DIMMING_LIGHT_DEBOUNCE);
+    }
+
+    private void accumulateRecentLight(long time, float lux) {
+        final long timeDelta = time - mLastLightSampleTime;
+        mRecentLightSamples += 1;
+        mRecentLightAverage += (lux - mRecentLightAverage) *
+                timeDelta / (mRecentLightTimeConstant + timeDelta);
+        mLastLightSample = lux;
+        mLastLightSampleTime = time;
+    }
+
+    private void debounceLightSensor() {
+        if (mLightMeasurementValid && mRecentLightSamples >= 1) {
+            final long now = SystemClock.uptimeMillis();
+            if (mPendingLightSensorDebounceTime <= now) {
+                accumulateRecentLight(now, mLastLightSample);
+                mLightMeasurement = mRecentLightAverage;
+
+                if (DEBUG) {
+                    Slog.d(TAG, "debounceLightSensor: Accepted new measurement "
+                            + mLightMeasurement + " after "
+                            + (now - mFirstRecentLightSampleTime) + " ms based on "
+                            + mRecentLightSamples + " recent samples.");
+                }
+
+                updateAutoBrightness(true);
+
+                // Now that we have debounced the light sensor data, we have the
+                // option of either leaving the sensor in a debounced state or
+                // restarting the debounce cycle by setting mRecentLightSamples to 0.
+                //
+                // If we leave the sensor debounced, then new average light measurements
+                // may be accepted immediately as long as they are trending in the same
+                // direction as they were before.  If the measurements start
+                // jittering or trending in the opposite direction then the debounce
+                // cycle will automatically be restarted.  The benefit is that the
+                // auto-brightness control can be more responsive to changes over a
+                // broad range.
+                //
+                // For now, we choose to be more responsive and leave the following line
+                // commented out.
+                //
+                // mRecentLightSamples = 0;
+            } else {
+                Message msg = mHandler.obtainMessage(MSG_LIGHT_SENSOR_DEBOUNCED);
+                msg.setAsynchronous(true);
+                mHandler.sendMessageAtTime(msg, mPendingLightSensorDebounceTime);
+            }
+        }
+    }
+
+    private void updateAutoBrightness(boolean sendUpdate) {
+        if (!mLightMeasurementValid) {
+            return;
+        }
+
+        final int newScreenAutoBrightness = interpolateBrightness(
+                mScreenAutoBrightnessSpline, mLightMeasurement);
+        if (mScreenAutoBrightness != newScreenAutoBrightness) {
+            if (DEBUG) {
+                Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
+                        + mScreenAutoBrightness + "newScreenAutoBrightness="
+                        + newScreenAutoBrightness);
+            }
+
+            mScreenAutoBrightness = newScreenAutoBrightness;
+            if (sendUpdate) {
+                sendUpdatePowerState();
+            }
+        }
+    }
+
+    private static int interpolateBrightness(Spline spline, float lux) {
+        return Math.min(255, Math.max(0, (int)Math.round(spline.interpolate(lux))));
+    }
+
+    private void sendOnStateChanged() {
+        mCallbackHandler.post(mOnStateChangedRunnable);
+    }
+
+    private final Runnable mOnStateChangedRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mCallbacks.onStateChanged();
+        }
+    };
+
+    private void sendOnProximityNegative() {
+        mCallbackHandler.post(mOnProximityNegativeRunnable);
+    }
+
+    private final Runnable mOnProximityNegativeRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mCallbacks.onProximityNegative();
+        }
+    };
+
+    public void dump(PrintWriter pw) {
+        synchronized (mLock) {
+            pw.println();
+            pw.println("Display Controller Locked State:");
+            pw.println("  mDisplayReadyLocked=" + mDisplayReadyLocked);
+            pw.println("  mPendingRequestLocked=" + mPendingRequestLocked);
+            pw.println("  mPendingRequestChangedLocked=" + mPendingRequestChangedLocked);
+            pw.println("  mPendingWaitForNegativeProximityLocked="
+                    + mPendingWaitForNegativeProximityLocked);
+            pw.println("  mPendingUpdatePowerStateLocked=" + mPendingUpdatePowerStateLocked);
+        }
+
+        pw.println();
+        pw.println("Display Controller Configuration:");
+        pw.println("  mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
+        pw.println("  mUseSoftwareAutoBrightnessConfig="
+                + mUseSoftwareAutoBrightnessConfig);
+        pw.println("  mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
+        pw.println("  mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
+
+        if (Looper.myLooper() == mHandler.getLooper()) {
+            dumpLocal(pw);
+        } else {
+            final StringWriter out = new StringWriter();
+            final CountDownLatch latch = new CountDownLatch(1);
+            Message msg = Message.obtain(mHandler,  new Runnable() {
+                @Override
+                public void run() {
+                    PrintWriter localpw = new PrintWriter(out);
+                    try {
+                        dumpLocal(localpw);
+                    } finally {
+                        localpw.flush();
+                        latch.countDown();
+                    }
+                }
+            });
+            msg.setAsynchronous(true);
+            mHandler.sendMessage(msg);
+            try {
+                latch.await();
+                pw.print(out.toString());
+            } catch (InterruptedException ex) {
+                pw.println();
+                pw.println("Failed to dump thread state due to interrupted exception!");
+            }
+        }
+    }
+
+    private void dumpLocal(PrintWriter pw) {
+        pw.println();
+        pw.println("Display Controller Thread State:");
+        pw.println("  mPowerRequest=" + mPowerRequest);
+        pw.println("  mWaitingForNegativeProximity=" + mWaitingForNegativeProximity);
+
+        pw.println("  mProximitySensor=" + mProximitySensor);
+        pw.println("  mProximitySensorEnabled=" + mProximitySensorEnabled);
+        pw.println("  mProximityThreshold=" + mProximityThreshold);
+        pw.println("  mProximity=" + proximityToString(mProximity));
+        pw.println("  mPendingProximity=" + proximityToString(mPendingProximity));
+        pw.println("  mPendingProximityDebounceTime="
+                + TimeUtils.formatUptime(mPendingProximityDebounceTime));
+        pw.println("  mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity);
+
+        pw.println("  mLightSensor=" + mLightSensor);
+        pw.println("  mLightSensorEnabled=" + mLightSensorEnabled);
+        pw.println("  mLightSensorEnableTime="
+                + TimeUtils.formatUptime(mLightSensorEnableTime));
+        pw.println("  mLightMeasurement=" + mLightMeasurement);
+        pw.println("  mLightMeasurementValid=" + mLightMeasurementValid);
+        pw.println("  mLastLightSample=" + mLastLightSample);
+        pw.println("  mLastLightSampleTime="
+                + TimeUtils.formatUptime(mLastLightSampleTime));
+        pw.println("  mRecentLightSamples=" + mRecentLightSamples);
+        pw.println("  mRecentLightAverage=" + mRecentLightAverage);
+        pw.println("  mRecentLightBrightening=" + mRecentLightBrightening);
+        pw.println("  mRecentLightTimeConstant=" + mRecentLightTimeConstant);
+        pw.println("  mFirstRecentLightSampleTime="
+                + TimeUtils.formatUptime(mFirstRecentLightSampleTime));
+        pw.println("  mPendingLightSensorDebounceTime="
+                + TimeUtils.formatUptime(mPendingLightSensorDebounceTime));
+        pw.println("  mScreenAutoBrightness=" + mScreenAutoBrightness);
+        pw.println("  mUsingScreenAutoBrightness=" + mUsingScreenAutoBrightness);
+
+        if (mElectronBeamOnAnimator != null) {
+            pw.println("  mElectronBeamOnAnimator.isStarted()=" +
+                    mElectronBeamOnAnimator.isStarted());
+        }
+        if (mElectronBeamOffAnimator != null) {
+            pw.println("  mElectronBeamOffAnimator.isStarted()=" +
+                    mElectronBeamOffAnimator.isStarted());
+        }
+
+        if (mPowerState != null) {
+            mPowerState.dump(pw);
+        }
+    }
+
+    private static String proximityToString(int state) {
+        switch (state) {
+            case PROXIMITY_UNKNOWN:
+                return "Unknown";
+            case PROXIMITY_NEGATIVE:
+                return "Negative";
+            case PROXIMITY_POSITIVE:
+                return "Positive";
+            default:
+                return Integer.toString(state);
+        }
+    }
+
+    private static boolean wantScreenOn(int state) {
+        switch (state) {
+            case DisplayPowerRequest.SCREEN_STATE_BRIGHT:
+            case DisplayPowerRequest.SCREEN_STATE_DIM:
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Asynchronous callbacks from the power controller to the power manager service.
+     */
+    public interface Callbacks {
+        void onStateChanged();
+        void onProximityNegative();
+    }
+
+    private final class DisplayControllerHandler extends Handler {
+        public DisplayControllerHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_UPDATE_POWER_STATE:
+                    updatePowerState();
+                    break;
+
+                case MSG_PROXIMITY_SENSOR_DEBOUNCED:
+                    debounceProximitySensor();
+                    break;
+
+                case MSG_LIGHT_SENSOR_DEBOUNCED:
+                    debounceLightSensor();
+                    break;
+            }
+        }
+    }
+
+    private final SensorEventListener mProximitySensorListener = new SensorEventListener() {
+        @Override
+        public void onSensorChanged(SensorEvent event) {
+            if (mProximitySensorEnabled) {
+                final long time = SystemClock.uptimeMillis();
+                final float distance = event.values[0];
+                boolean positive = distance >= 0.0f && distance < mProximityThreshold;
+                handleProximitySensorEvent(time, positive);
+            }
+        }
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+            // Not used.
+        }
+    };
+
+    private final SensorEventListener mLightSensorListener = new SensorEventListener() {
+        @Override
+        public void onSensorChanged(SensorEvent event) {
+            if (mLightSensorEnabled) {
+                final long time = SystemClock.uptimeMillis();
+                final float lux = event.values[0];
+                handleLightSensorEvent(time, lux);
+            }
+        }
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+            // Not used.
+        }
+    };
+}
diff --git a/services/java/com/android/server/power/DisplayPowerRequest.java b/services/java/com/android/server/power/DisplayPowerRequest.java
new file mode 100644
index 0000000..7e4607e
--- /dev/null
+++ b/services/java/com/android/server/power/DisplayPowerRequest.java
@@ -0,0 +1,96 @@
+/*
+ * 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;
+
+import android.os.PowerManager;
+
+/**
+ * Describes the requested power state of the display.
+ *
+ * This object is intended to describe the general characteristics of the
+ * power state, such as whether the screen should be on or off and the current
+ * brightness controls leaving the {@link DisplayPowerController} to manage the
+ * details of how the transitions between states should occur.  The goal is for
+ * the {@link PowerManagerService} to focus on the global power state and not
+ * have to micro-manage screen off animations, auto-brightness and other effects.
+ */
+final class DisplayPowerRequest {
+    public static final int SCREEN_STATE_OFF = 0;
+    public static final int SCREEN_STATE_DIM = 1;
+    public static final int SCREEN_STATE_BRIGHT = 2;
+
+    // The requested minimum screen power state: off, dim or bright.
+    public int screenState;
+
+    // If true, the proximity sensor overrides the screen state when an object is
+    // nearby, turning it off temporarily until the object is moved away.
+    public boolean useProximitySensor;
+
+    // The desired screen brightness in the range 0 (minimum / off) to 255 (brightest).
+    // The display power controller may choose to clamp the brightness.
+    // When auto-brightness is enabled, this field should specify a nominal default
+    // value to use while waiting for the light sensor to report enough data.
+    public int screenBrightness;
+
+    // If true, enables automatic brightness control.
+    public boolean useAutoBrightness;
+
+    public DisplayPowerRequest() {
+        screenState = SCREEN_STATE_BRIGHT;
+        useProximitySensor = false;
+        screenBrightness = PowerManager.BRIGHTNESS_ON;
+        useAutoBrightness = false;
+    }
+
+    public DisplayPowerRequest(DisplayPowerRequest other) {
+        copyFrom(other);
+    }
+
+    public void copyFrom(DisplayPowerRequest other) {
+        screenState = other.screenState;
+        useProximitySensor = other.useProximitySensor;
+        screenBrightness = other.screenBrightness;
+        useAutoBrightness = other.useAutoBrightness;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof DisplayPowerRequest
+                && equals((DisplayPowerRequest)o);
+    }
+
+    public boolean equals(DisplayPowerRequest other) {
+        return other != null
+                && screenState == other.screenState
+                && useProximitySensor == other.useProximitySensor
+                && screenBrightness == other.screenBrightness
+                && useAutoBrightness == other.useAutoBrightness;
+    }
+
+    @Override
+    public int hashCode() {
+        return 0; // don't care
+    }
+
+    @Override
+    public String toString() {
+        return "screenState=" + screenState
+                + ", useProximitySensor=" + useProximitySensor
+                + ", screenBrightness=" + screenBrightness
+                + ", useAutoBrightness=" + useAutoBrightness;
+    }
+}
diff --git a/services/java/com/android/server/power/DisplayPowerState.java b/services/java/com/android/server/power/DisplayPowerState.java
new file mode 100644
index 0000000..64a0462
--- /dev/null
+++ b/services/java/com/android/server/power/DisplayPowerState.java
@@ -0,0 +1,270 @@
+/*
+ * 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;
+
+import android.os.Looper;
+import android.os.PowerManager;
+import android.util.FloatProperty;
+import android.util.IntProperty;
+import android.util.Slog;
+import android.view.Choreographer;
+
+import java.io.PrintWriter;
+
+/**
+ * Represents the current display power state and realizes it.
+ *
+ * This component is similar in nature to a {@link View} except that it describes
+ * the properties of a display.  When properties are changed, the component
+ * invalidates itself and posts a callback to the {@link Choreographer} to
+ * apply the changes.  This mechanism enables the display power state to be
+ * animated smoothly by the animation framework.
+ *
+ * This component must only be created or accessed by the {@link Looper} thread
+ * that belongs to the {@link DisplayPowerController}.
+ *
+ * We don't need to worry about holding a suspend blocker here because the
+ * {@link DisplayPowerController} does that for us whenever there is a pending invalidate.
+ */
+final class DisplayPowerState {
+    private static final String TAG = "DisplayPowerState";
+
+    private static boolean DEBUG = false;
+
+    private static final int DIRTY_SCREEN_ON = 1 << 0;
+    private static final int DIRTY_ELECTRON_BEAM = 1 << 1;
+    private static final int DIRTY_BRIGHTNESS = 1 << 2;
+
+    private static final int DIRTY_ALL = 0xffffffff;
+
+    private final Choreographer mChoreographer;
+    private final ElectronBeam mElectronBeam;
+    private final PhotonicModulator mScreenBrightnessModulator;
+
+    private int mDirty;
+    private boolean mScreenOn;
+    private float mElectronBeamLevel;
+    private int mScreenBrightness;
+
+    private Runnable mCleanListener;
+
+    public DisplayPowerState(ElectronBeam electronBean,
+            PhotonicModulator screenBrightnessModulator) {
+        mChoreographer = Choreographer.getInstance();
+        mElectronBeam = electronBean;
+        mScreenBrightnessModulator = screenBrightnessModulator;
+
+        mScreenOn = true;
+        mElectronBeamLevel = 1.0f;
+        mScreenBrightness = PowerManager.BRIGHTNESS_ON;
+        invalidate(DIRTY_ALL);
+    }
+
+    public static final FloatProperty<DisplayPowerState> ELECTRON_BEAM_LEVEL =
+            new FloatProperty<DisplayPowerState>("electronBeamLevel") {
+        @Override
+        public void setValue(DisplayPowerState object, float value) {
+            object.setElectronBeamLevel(value);
+        }
+
+        @Override
+        public Float get(DisplayPowerState object) {
+            return object.getElectronBeamLevel();
+        }
+    };
+
+    public static final IntProperty<DisplayPowerState> SCREEN_BRIGHTNESS =
+            new IntProperty<DisplayPowerState>("screenBrightness") {
+        @Override
+        public void setValue(DisplayPowerState object, int value) {
+            object.setScreenBrightness(value);
+        }
+
+        @Override
+        public Integer get(DisplayPowerState object) {
+            return object.getScreenBrightness();
+        }
+    };
+
+    /**
+     * Sets whether the screen is on or off.
+     */
+    public void setScreenOn(boolean on) {
+        if (mScreenOn != on) {
+            if (DEBUG) {
+                Slog.d(TAG, "setScreenOn: on=" + on);
+            }
+
+            mScreenOn = on;
+            invalidate(DIRTY_SCREEN_ON);
+        }
+    }
+
+    /**
+     * Returns true if the screen is on.
+     */
+    public boolean isScreenOn() {
+        return mScreenOn;
+    }
+
+    /**
+     * Prepares the electron beam to turn on or off.
+     * This method should be called before starting an animation because it
+     * can take a fair amount of time to prepare the electron beam surface.
+     *
+     * @param warmUp True if the electron beam should start warming up.
+     * @return True if the electron beam was prepared.
+     */
+    public boolean prepareElectronBeam(boolean warmUp) {
+        boolean success = mElectronBeam.prepare(warmUp);
+        invalidate(DIRTY_ELECTRON_BEAM);
+        return success;
+    }
+
+    /**
+     * Dismisses the electron beam surface.
+     */
+    public void dismissElectronBeam() {
+        mElectronBeam.dismiss();
+    }
+
+    /**
+     * Sets the level of the electron beam steering current.
+     *
+     * The display is blanked when the level is 0.0.  In normal use, the electron
+     * beam should have a value of 1.0.  The electron beam is unstable in between
+     * these states and the picture quality may be compromised.  For best effect,
+     * the electron beam should be warmed up or cooled off slowly.
+     *
+     * Warning: Electron beam emits harmful radiation.  Avoid direct exposure to
+     * skin or eyes.
+     *
+     * @param level The level, ranges from 0.0 (full off) to 1.0 (full on).
+     */
+    public void setElectronBeamLevel(float level) {
+        if (mElectronBeamLevel != level) {
+            if (DEBUG) {
+                Slog.d(TAG, "setElectronBeamLevel: level=" + level);
+            }
+
+            mElectronBeamLevel = level;
+            invalidate(DIRTY_ELECTRON_BEAM);
+        }
+    }
+
+    /**
+     * Gets the level of the electron beam steering current.
+     */
+    public float getElectronBeamLevel() {
+        return mElectronBeamLevel;
+    }
+
+    /**
+     * Sets the display brightness.
+     *
+     * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest).
+     */
+    public void setScreenBrightness(int brightness) {
+        if (mScreenBrightness != brightness) {
+            if (DEBUG) {
+                Slog.d(TAG, "setScreenBrightness: brightness=" + brightness);
+            }
+
+            mScreenBrightness = brightness;
+            invalidate(DIRTY_BRIGHTNESS);
+        }
+    }
+
+    /**
+     * Gets the screen brightness.
+     */
+    public int getScreenBrightness() {
+        return mScreenBrightness;
+    }
+
+    /**
+     * Returns true if no properties have been invalidated.
+     * Otherwise, returns false and promises to invoke the specified listener
+     * when the properties have all been applied.
+     * The listener always overrides any previously set listener.
+     */
+    public boolean waitUntilClean(Runnable listener) {
+        if (mDirty != 0) {
+            mCleanListener = listener;
+            return false;
+        } else {
+            mCleanListener = null;
+            return true;
+        }
+    }
+
+    public void dump(PrintWriter pw) {
+        pw.println();
+        pw.println("Display Power State:");
+        pw.println("  mDirty=" + Integer.toHexString(mDirty));
+        pw.println("  mScreenOn=" + mScreenOn);
+        pw.println("  mScreenBrightness=" + mScreenBrightness);
+        pw.println("  mElectronBeamLevel=" + mElectronBeamLevel);
+
+        mElectronBeam.dump(pw);
+    }
+
+    private void invalidate(int dirty) {
+        if (mDirty == 0) {
+            mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL,
+                    mTraversalRunnable, null);
+        }
+
+        mDirty |= dirty;
+    }
+
+    private void apply() {
+        if (mDirty != 0) {
+            if ((mDirty & DIRTY_SCREEN_ON) != 0 && !mScreenOn) {
+                PowerManagerService.nativeSetScreenState(false);
+            }
+
+            if ((mDirty & DIRTY_ELECTRON_BEAM) != 0) {
+                mElectronBeam.draw(mElectronBeamLevel);
+            }
+
+            if ((mDirty & (DIRTY_BRIGHTNESS | DIRTY_SCREEN_ON | DIRTY_ELECTRON_BEAM)) != 0) {
+                mScreenBrightnessModulator.setBrightness(mScreenOn ?
+                        (int)(mScreenBrightness * mElectronBeamLevel) : 0);
+            }
+
+            if ((mDirty & DIRTY_SCREEN_ON) != 0 && mScreenOn) {
+                PowerManagerService.nativeSetScreenState(true);
+            }
+
+            mDirty = 0;
+
+            if (mCleanListener != null) {
+                mCleanListener.run();
+            }
+        }
+    }
+
+    private final Runnable mTraversalRunnable = new Runnable() {
+        @Override
+        public void run() {
+            if (mDirty != 0) {
+                apply();
+            }
+        }
+    };
+}
diff --git a/services/java/com/android/server/power/ElectronBeam.java b/services/java/com/android/server/power/ElectronBeam.java
new file mode 100644
index 0000000..5472148
--- /dev/null
+++ b/services/java/com/android/server/power/ElectronBeam.java
@@ -0,0 +1,652 @@
+/*
+ * 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;
+
+import android.graphics.Bitmap;
+import android.graphics.PixelFormat;
+import android.opengl.EGL14;
+import android.opengl.EGLConfig;
+import android.opengl.EGLContext;
+import android.opengl.EGLDisplay;
+import android.opengl.EGLSurface;
+import android.opengl.GLES10;
+import android.opengl.GLUtils;
+import android.os.Looper;
+import android.os.Process;
+import android.util.FloatMath;
+import android.util.Slog;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.Surface;
+import android.view.SurfaceSession;
+import android.view.WindowManagerImpl;
+
+import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+/**
+ * Bzzzoooop!  *crackle*
+ *
+ * Animates a screen transition from on to off or off to on by applying
+ * some GL transformations to a screenshot.
+ *
+ * This component must only be created or accessed by the {@link Looper} thread
+ * that belongs to the {@link DisplayPowerController}.
+ */
+final class ElectronBeam {
+    private static final String TAG = "ElectronBeam";
+
+    private static final boolean DEBUG = false;
+
+    // The layer for the electron beam surface.
+    // This is currently hardcoded to be one layer above the boot animation.
+    private static final int ELECTRON_BEAM_LAYER = 0x40000001;
+
+    // The relative proportion of the animation to spend performing
+    // the horizontal stretch effect.  The remainder is spent performing
+    // the vertical stretch effect.
+    private static final float HSTRETCH_DURATION = 0.3f;
+    private static final float VSTRETCH_DURATION = 1.0f - HSTRETCH_DURATION;
+
+    // Set to true when the animation context has been fully prepared.
+    private boolean mPrepared;
+    private boolean mWarmUp;
+
+    private final DisplayInfo mDisplayInfo = new DisplayInfo();
+    private int mDisplayLayerStack; // layer stack associated with primary display
+    private int mDisplayRotation;
+    private int mDisplayWidth;      // real width, not rotated
+    private int mDisplayHeight;     // real height, not rotated
+    private SurfaceSession mSurfaceSession;
+    private Surface mSurface;
+    private EGLDisplay mEglDisplay;
+    private EGLConfig mEglConfig;
+    private EGLContext mEglContext;
+    private EGLSurface mEglSurface;
+    private boolean mSurfaceVisible;
+
+    // Texture names.  We only use one texture, which contains the screenshot.
+    private final int[] mTexNames = new int[1];
+    private boolean mTexNamesGenerated;
+
+    // Vertex and corresponding texture coordinates.
+    // We have 4 2D vertices, so 8 elements.  The vertices form a quad.
+    private final FloatBuffer mVertexBuffer = createNativeFloatBuffer(8);
+    private final FloatBuffer mTexCoordBuffer = createNativeFloatBuffer(8);
+
+    public ElectronBeam() {
+    }
+
+    /**
+     * Warms up the electron beam in preparation for turning on or off.
+     * This method prepares a GL context, and captures a screen shot.
+     *
+     * @param warmUp True if the electron beam is about to be turned on, false if
+     * it is about to be turned off.
+     * @return True if the electron beam is ready, false if it is uncontrollable.
+     */
+    public boolean prepare(boolean warmUp) {
+        if (DEBUG) {
+            Slog.d(TAG, "prepare: warmUp=" + warmUp);
+        }
+
+        mWarmUp = warmUp;
+
+        // Get the display size and adjust it for rotation.
+        Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
+        display.getDisplayInfo(mDisplayInfo);
+        mDisplayLayerStack = display.getDisplayId();
+        mDisplayRotation = mDisplayInfo.rotation;
+        if (mDisplayRotation == Surface.ROTATION_90
+                || mDisplayRotation == Surface.ROTATION_270) {
+            mDisplayWidth = mDisplayInfo.logicalHeight;
+            mDisplayHeight = mDisplayInfo.logicalWidth;
+        } else {
+            mDisplayWidth = mDisplayInfo.logicalWidth;
+            mDisplayHeight = mDisplayInfo.logicalHeight;
+        }
+
+        // Prepare the surface for drawing.
+        if (!createEglContext()
+                || !createEglSurface()
+                || !captureScreenshotTextureAndSetViewport()) {
+            dismiss();
+            return false;
+        }
+
+        mPrepared = true;
+        return true;
+    }
+
+    /**
+     * Dismisses the electron beam animation surface and cleans up.
+     *
+     * To prevent stray photons from leaking out after the electron beam has been
+     * turned off, it is a good idea to defer dismissing the animation until the
+     * electron beam has been turned back on fully.
+     */
+    public void dismiss() {
+        if (DEBUG) {
+            Slog.d(TAG, "dismiss");
+        }
+
+        destroyScreenshotTexture();
+        destroyEglSurface();
+        mPrepared = false;
+    }
+
+    /**
+     * Draws an animation frame showing the electron beam activated at the
+     * specified level.
+     *
+     * @param level The electron beam level.
+     * @return True if successful.
+     */
+    public boolean draw(float level) {
+        if (DEBUG) {
+            Slog.d(TAG, "drawFrame: level=" + level);
+        }
+
+        if (!attachEglContext()) {
+            return false;
+        }
+        try {
+            // Clear frame to solid black.
+            GLES10.glClearColor(0f, 0f, 0f, 1f);
+            GLES10.glClear(GLES10.GL_COLOR_BUFFER_BIT);
+
+            // Draw the frame.
+            if (level < HSTRETCH_DURATION) {
+                drawHStretch(1.0f - (level / HSTRETCH_DURATION));
+            } else {
+                drawVStretch(1.0f - ((level - HSTRETCH_DURATION) / VSTRETCH_DURATION));
+            }
+            if (checkGlErrors("drawFrame")) {
+                return false;
+            }
+
+            EGL14.eglSwapBuffers(mEglDisplay, mEglSurface);
+        } finally {
+            detachEglContext();
+        }
+
+        return showEglSurface();
+    }
+
+    /**
+     * Draws a frame where the content of the electron beam is collapsing inwards upon
+     * itself vertically with red / green / blue channels dispersing and eventually
+     * merging down to a single horizontal line.
+     *
+     * @param stretch The stretch factor.  0.0 is no collapse, 1.0 is full collapse.
+     */
+    private void drawVStretch(float stretch) {
+        // compute interpolation scale factors for each color channel
+        final float ar = scurve(stretch, 7.5f);
+        final float ag = scurve(stretch, 8.0f);
+        final float ab = scurve(stretch, 8.5f);
+        if (DEBUG) {
+            Slog.d(TAG, "drawVStretch: stretch=" + stretch
+                    + ", ar=" + ar + ", ag=" + ag + ", ab=" + ab);
+        }
+
+        // set blending
+        GLES10.glBlendFunc(GLES10.GL_ONE, GLES10.GL_ONE);
+        GLES10.glEnable(GLES10.GL_BLEND);
+
+        // bind vertex buffer
+        GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer);
+        GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY);
+
+        // bind texture and set blending for drawing planes
+        GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]);
+        GLES10.glTexEnvx(GLES10.GL_TEXTURE_ENV, GLES10.GL_TEXTURE_ENV_MODE,
+                mWarmUp ? GLES10.GL_MODULATE : GLES10.GL_REPLACE);
+        GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
+                GLES10.GL_TEXTURE_MAG_FILTER, GLES10.GL_LINEAR);
+        GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
+                GLES10.GL_TEXTURE_MIN_FILTER, GLES10.GL_LINEAR);
+        GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
+                GLES10.GL_TEXTURE_WRAP_S, GLES10.GL_CLAMP_TO_EDGE);
+        GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
+                GLES10.GL_TEXTURE_WRAP_T, GLES10.GL_CLAMP_TO_EDGE);
+        GLES10.glEnable(GLES10.GL_TEXTURE_2D);
+        GLES10.glTexCoordPointer(2, GLES10.GL_FLOAT, 0, mTexCoordBuffer);
+        GLES10.glEnableClientState(GLES10.GL_TEXTURE_COORD_ARRAY);
+
+        // draw the red plane
+        setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ar);
+        GLES10.glColorMask(true, false, false, true);
+        GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
+
+        // draw the green plane
+        setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag);
+        GLES10.glColorMask(false, true, false, true);
+        GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
+
+        // draw the blue plane
+        setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ab);
+        GLES10.glColorMask(false, false, true, true);
+        GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
+
+        // clean up after drawing planes
+        GLES10.glDisable(GLES10.GL_TEXTURE_2D);
+        GLES10.glDisableClientState(GLES10.GL_TEXTURE_COORD_ARRAY);
+        GLES10.glColorMask(true, true, true, true);
+
+        // draw the white highlight (we use the last vertices)
+        if (!mWarmUp) {
+            GLES10.glColor4f(ag, ag, ag, 1.0f);
+            GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
+        }
+
+        // clean up
+        GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY);
+        GLES10.glDisable(GLES10.GL_BLEND);
+    }
+
+    /**
+     * Draws a frame where the electron beam has been stretched out into
+     * a thin white horizontal line that fades as it expands outwards.
+     *
+     * @param stretch The stretch factor.  0.0 is no stretch / no fade,
+     * 1.0 is maximum stretch / maximum fade.
+     */
+    private void drawHStretch(float stretch) {
+        // compute interpolation scale factor
+        final float ag = scurve(stretch, 8.0f);
+        if (DEBUG) {
+            Slog.d(TAG, "drawHStretch: stretch=" + stretch + ", ag=" + ag);
+        }
+
+        if (stretch < 1.0f) {
+            // bind vertex buffer
+            GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer);
+            GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY);
+
+            // draw narrow fading white line
+            setHStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag);
+            GLES10.glColor4f(1.0f - ag, 1.0f - ag, 1.0f - ag, 1.0f);
+            GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
+
+            // clean up
+            GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY);
+        }
+    }
+
+    private static void setVStretchQuad(FloatBuffer vtx, float dw, float dh, float a) {
+        final float w = dw + (dw * a);
+        final float h = dh - (dh * a);
+        final float x = (dw - w) * 0.5f;
+        final float y = (dh - h) * 0.5f;
+        setQuad(vtx, x, y, w, h);
+    }
+
+    private static void setHStretchQuad(FloatBuffer vtx, float dw, float dh, float a) {
+        final float w = dw + (dw * a);
+        final float h = 1.0f;
+        final float x = (dw - w) * 0.5f;
+        final float y = (dh - h) * 0.5f;
+        setQuad(vtx, x, y, w, h);
+    }
+
+    private static void setQuad(FloatBuffer vtx, float x, float y, float w, float h) {
+        if (DEBUG) {
+            Slog.d(TAG, "setQuad: x=" + x + ", y=" + y + ", w=" + w + ", h=" + h);
+        }
+        vtx.put(0, x);
+        vtx.put(1, y);
+        vtx.put(2, x);
+        vtx.put(3, y + h);
+        vtx.put(4, x + w);
+        vtx.put(5, y + h);
+        vtx.put(6, x + w);
+        vtx.put(7, y);
+    }
+
+    private boolean captureScreenshotTextureAndSetViewport() {
+        // TODO: Use a SurfaceTexture to avoid the extra texture upload.
+        Bitmap bitmap = Surface.screenshot(mDisplayWidth, mDisplayHeight,
+                0, ELECTRON_BEAM_LAYER - 1);
+        if (bitmap == null) {
+            Slog.e(TAG, "Could not take a screenshot!");
+            return false;
+        }
+        try {
+            if (!attachEglContext()) {
+                return false;
+            }
+            try {
+                if (!mTexNamesGenerated) {
+                    GLES10.glGenTextures(1, mTexNames, 0);
+                    if (checkGlErrors("glGenTextures")) {
+                        return false;
+                    }
+                    mTexNamesGenerated = true;
+                }
+
+                GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]);
+                if (checkGlErrors("glBindTexture")) {
+                    return false;
+                }
+
+                float u = 1.0f;
+                float v = 1.0f;
+                GLUtils.texImage2D(GLES10.GL_TEXTURE_2D, 0, bitmap, 0);
+                if (checkGlErrors("glTexImage2D, first try", false)) {
+                    // Try a power of two size texture instead.
+                    int tw = nextPowerOfTwo(mDisplayWidth);
+                    int th = nextPowerOfTwo(mDisplayHeight);
+                    int format = GLUtils.getInternalFormat(bitmap);
+                    GLES10.glTexImage2D(GLES10.GL_TEXTURE_2D, 0,
+                            format, tw, th, 0,
+                            format, GLES10.GL_UNSIGNED_BYTE, null);
+                    if (checkGlErrors("glTexImage2D, second try")) {
+                        return false;
+                    }
+
+                    GLUtils.texSubImage2D(GLES10.GL_TEXTURE_2D, 0, 0, 0, bitmap);
+                    if (checkGlErrors("glTexSubImage2D")) {
+                        return false;
+                    }
+
+                    u = (float)mDisplayWidth / tw;
+                    v = (float)mDisplayHeight / th;
+                }
+
+                // Set up texture coordinates for a quad.
+                // We might need to change this if the texture ends up being
+                // a different size from the display for some reason.
+                mTexCoordBuffer.put(0, 0f);
+                mTexCoordBuffer.put(1, v);
+                mTexCoordBuffer.put(2, 0f);
+                mTexCoordBuffer.put(3, 0f);
+                mTexCoordBuffer.put(4, u);
+                mTexCoordBuffer.put(5, 0f);
+                mTexCoordBuffer.put(6, u);
+                mTexCoordBuffer.put(7, v);
+
+                // Set up our viewport.
+                GLES10.glViewport(0, 0, mDisplayWidth, mDisplayHeight);
+                GLES10.glMatrixMode(GLES10.GL_PROJECTION);
+                GLES10.glLoadIdentity();
+                GLES10.glOrthof(0, mDisplayWidth, 0, mDisplayHeight, 0, 1);
+                GLES10.glMatrixMode(GLES10.GL_MODELVIEW);
+                GLES10.glLoadIdentity();
+                GLES10.glMatrixMode(GLES10.GL_TEXTURE);
+                GLES10.glLoadIdentity();
+            } finally {
+                detachEglContext();
+            }
+        } finally {
+            bitmap.recycle();
+        }
+        return true;
+    }
+
+    private void destroyScreenshotTexture() {
+        if (mTexNamesGenerated) {
+            mTexNamesGenerated = false;
+            if (attachEglContext()) {
+                try {
+                    GLES10.glDeleteTextures(1, mTexNames, 0);
+                    checkGlErrors("glDeleteTextures");
+                } finally {
+                    detachEglContext();
+                }
+            }
+        }
+    }
+
+    private boolean createEglContext() {
+        if (mEglDisplay == null) {
+            mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
+            if (mEglDisplay == EGL14.EGL_NO_DISPLAY) {
+                logEglError("eglGetDisplay");
+                return false;
+            }
+
+            int[] version = new int[2];
+            if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) {
+                mEglDisplay = null;
+                logEglError("eglInitialize");
+                return false;
+            }
+        }
+
+        if (mEglConfig == null) {
+            int[] eglConfigAttribList = new int[] {
+                    EGL14.EGL_RED_SIZE, 8,
+                    EGL14.EGL_GREEN_SIZE, 8,
+                    EGL14.EGL_BLUE_SIZE, 8,
+                    EGL14.EGL_ALPHA_SIZE, 8,
+                    EGL14.EGL_NONE
+            };
+            int[] numEglConfigs = new int[1];
+            EGLConfig[] eglConfigs = new EGLConfig[1];
+            if (!EGL14.eglChooseConfig(mEglDisplay, eglConfigAttribList, 0,
+                    eglConfigs, 0, eglConfigs.length, numEglConfigs, 0)) {
+                logEglError("eglChooseConfig");
+                return false;
+            }
+            mEglConfig = eglConfigs[0];
+        }
+
+        if (mEglContext == null) {
+            int[] eglContextAttribList = new int[] {
+                    EGL14.EGL_NONE
+            };
+            mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig,
+                    EGL14.EGL_NO_CONTEXT, eglContextAttribList, 0);
+            if (mEglContext == null) {
+                logEglError("eglCreateContext");
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /* not used because it is too expensive to create / destroy contexts all of the time
+    private void destroyEglContext() {
+        if (mEglContext != null) {
+            if (!EGL14.eglDestroyContext(mEglDisplay, mEglContext)) {
+                logEglError("eglDestroyContext");
+            }
+            mEglContext = null;
+        }
+    }*/
+
+    private boolean createEglSurface() {
+        if (mSurfaceSession == null) {
+            mSurfaceSession = new SurfaceSession();
+        }
+
+        Surface.openTransaction();
+        try {
+            if (mSurface == null) {
+                try {
+                    mSurface = new Surface(mSurfaceSession, Process.myPid(),
+                            "ElectronBeam", mDisplayLayerStack, mDisplayWidth, mDisplayHeight,
+                            PixelFormat.OPAQUE, Surface.OPAQUE | Surface.HIDDEN);
+                } catch (Surface.OutOfResourcesException ex) {
+                    Slog.e(TAG, "Unable to create surface.", ex);
+                    return false;
+                }
+            }
+
+            mSurface.setSize(mDisplayWidth, mDisplayHeight);
+
+            switch (mDisplayRotation) {
+                case Surface.ROTATION_0:
+                    mSurface.setPosition(0, 0);
+                    mSurface.setMatrix(1, 0, 0, 1);
+                    break;
+                case Surface.ROTATION_90:
+                    mSurface.setPosition(0, mDisplayWidth);
+                    mSurface.setMatrix(0, -1, 1, 0);
+                    break;
+                case Surface.ROTATION_180:
+                    mSurface.setPosition(mDisplayWidth, mDisplayHeight);
+                    mSurface.setMatrix(-1, 0, 0, -1);
+                    break;
+                case Surface.ROTATION_270:
+                    mSurface.setPosition(mDisplayHeight, 0);
+                    mSurface.setMatrix(0, 1, -1, 0);
+                    break;
+            }
+        } finally {
+            Surface.closeTransaction();
+        }
+
+        if (mEglSurface == null) {
+            int[] eglSurfaceAttribList = new int[] {
+                    EGL14.EGL_NONE
+            };
+            mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface,
+                    eglSurfaceAttribList, 0);
+            if (mEglSurface == null) {
+                logEglError("eglCreateWindowSurface");
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void destroyEglSurface() {
+        if (mEglSurface != null) {
+            if (!EGL14.eglDestroySurface(mEglDisplay, mEglSurface)) {
+                logEglError("eglDestroySurface");
+            }
+            mEglSurface = null;
+        }
+
+        if (mSurface != null) {
+            Surface.openTransaction();
+            try {
+                mSurface.destroy();
+            } finally {
+                Surface.closeTransaction();
+            }
+            mSurface = null;
+            mSurfaceVisible = false;
+        }
+    }
+
+    private boolean showEglSurface() {
+        if (!mSurfaceVisible) {
+            Surface.openTransaction();
+            try {
+                mSurface.setLayer(ELECTRON_BEAM_LAYER);
+                mSurface.show();
+            } finally {
+                Surface.closeTransaction();
+            }
+            mSurfaceVisible = true;
+        }
+        return true;
+    }
+
+    private boolean attachEglContext() {
+        if (mEglSurface == null) {
+            return false;
+        }
+        if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+            logEglError("eglMakeCurrent");
+            return false;
+        }
+        return true;
+    }
+
+    private void detachEglContext() {
+        if (mEglDisplay != null) {
+            EGL14.eglMakeCurrent(mEglDisplay,
+                    EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
+        }
+    }
+
+    /**
+     * Interpolates a value in the range 0 .. 1 along a sigmoid curve
+     * yielding a result in the range 0 .. 1 scaled such that:
+     * scurve(0) == 0, scurve(0.5) == 0.5, scurve(1) == 1.
+     */
+    private static float scurve(float value, float s) {
+        // A basic sigmoid has the form y = 1.0f / FloatMap.exp(-x * s).
+        // Here we take the input datum and shift it by 0.5 so that the
+        // domain spans the range -0.5 .. 0.5 instead of 0 .. 1.
+        final float x = value - 0.5f;
+
+        // Next apply the sigmoid function to the scaled value
+        // which produces a value in the range 0 .. 1 so we subtract
+        // 0.5 to get a value in the range -0.5 .. 0.5 instead.
+        final float y = sigmoid(x, s) - 0.5f;
+
+        // To obtain the desired boundary conditions we need to scale
+        // the result so that it fills a range of -1 .. 1.
+        final float v = sigmoid(0.5f, s) - 0.5f;
+
+        // And finally remap the value back to a range of 0 .. 1.
+        return y / v * 0.5f + 0.5f;
+    }
+
+    private static float sigmoid(float x, float s) {
+        return 1.0f / (1.0f + FloatMath.exp(-x * s));
+    }
+
+    private static int nextPowerOfTwo(int value) {
+        return 1 << (32 - Integer.numberOfLeadingZeros(value));
+    }
+
+    private static FloatBuffer createNativeFloatBuffer(int size) {
+        ByteBuffer bb = ByteBuffer.allocateDirect(size * 4);
+        bb.order(ByteOrder.nativeOrder());
+        return bb.asFloatBuffer();
+    }
+
+    private static void logEglError(String func) {
+        Slog.e(TAG, func + " failed: error " + EGL14.eglGetError(), new Throwable());
+    }
+
+    private static boolean checkGlErrors(String func) {
+        return checkGlErrors(func, true);
+    }
+
+    private static boolean checkGlErrors(String func, boolean log) {
+        boolean hadError = false;
+        int error;
+        while ((error = GLES10.glGetError()) != GLES10.GL_NO_ERROR) {
+            if (log) {
+                Slog.e(TAG, func + " failed: error " + error, new Throwable());
+            }
+            hadError = true;
+        }
+        return hadError;
+    }
+
+    public void dump(PrintWriter pw) {
+        pw.println();
+        pw.println("Electron Beam State:");
+        pw.println("  mPrepared=" + mPrepared);
+        pw.println("  mWarmUp=" + mWarmUp);
+        pw.println("  mDisplayLayerStack=" + mDisplayLayerStack);
+        pw.println("  mDisplayRotation=" + mDisplayRotation);
+        pw.println("  mDisplayWidth=" + mDisplayWidth);
+        pw.println("  mDisplayHeight=" + mDisplayHeight);
+        pw.println("  mSurfaceVisible=" + mSurfaceVisible);
+    }
+}
diff --git a/services/java/com/android/server/power/Notifier.java b/services/java/com/android/server/power/Notifier.java
new file mode 100644
index 0000000..37384d2
--- /dev/null
+++ b/services/java/com/android/server/power/Notifier.java
@@ -0,0 +1,458 @@
+/*
+ * 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;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.server.EventLogTags;
+
+import android.app.ActivityManagerNative;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.WorkSource;
+import android.util.EventLog;
+import android.util.Slog;
+import android.view.WindowManagerPolicy;
+import android.view.WindowManagerPolicy.ScreenOnListener;
+
+/**
+ * Sends broadcasts about important power state changes.
+ *
+ * This methods of this class may be called by the power manager service while
+ * its lock is being held.  Internally it takes care of sending broadcasts to
+ * notify other components of the system or applications asynchronously.
+ *
+ * The notifier is designed to collapse unnecessary broadcasts when it is not
+ * possible for the system to have observed an intermediate state.
+ *
+ * For example, if the device wakes up, goes to sleep and wakes up again immediately
+ * before the go to sleep broadcast has been sent, then no broadcast will be
+ * sent about the system going to sleep and waking up.
+ */
+final class Notifier {
+    private static final String TAG = "PowerManagerNotifier";
+
+    private static final boolean DEBUG = false;
+
+    private static final int POWER_STATE_UNKNOWN = 0;
+    private static final int POWER_STATE_AWAKE = 1;
+    private static final int POWER_STATE_ASLEEP = 2;
+
+    private static final int MSG_USER_ACTIVITY = 1;
+    private static final int MSG_BROADCAST = 2;
+
+    private final Object mLock = new Object();
+
+    private final Context mContext;
+    private final IBatteryStats mBatteryStats;
+    private final SuspendBlocker mSuspendBlocker;
+    private final WindowManagerPolicy mPolicy;
+    private final ScreenOnListener mScreenOnListener;
+
+    private final NotifierHandler mHandler;
+    private final Intent mScreenOnIntent;
+    private final Intent mScreenOffIntent;
+
+    // The current power state.
+    private int mActualPowerState;
+    private int mLastGoToSleepReason;
+
+    // The currently broadcasted power state.  This reflects what other parts of the
+    // system have observed.
+    private int mBroadcastedPowerState;
+    private boolean mBroadcastInProgress;
+    private long mBroadcastStartTime;
+
+    // True if a user activity message should be sent.
+    private boolean mUserActivityPending;
+
+    public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
+            SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
+            ScreenOnListener screenOnListener) {
+        mContext = context;
+        mBatteryStats = batteryStats;
+        mSuspendBlocker = suspendBlocker;
+        mPolicy = policy;
+        mScreenOnListener = screenOnListener;
+
+        mHandler = new NotifierHandler(looper);
+        mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
+        mScreenOnIntent.addFlags(
+                Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
+        mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
+        mScreenOffIntent.addFlags(
+                Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
+    }
+
+    /**
+     * Called when a wake lock is acquired.
+     */
+    public void onWakeLockAcquired(int flags, String tag, int ownerUid, int ownerPid,
+            WorkSource workSource) {
+        if (DEBUG) {
+            Slog.d(TAG, "onWakeLockAcquired: flags=" + flags + ", tag=\"" + tag
+                    + "\", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+                    + ", workSource=" + workSource);
+        }
+
+        if (!isWakeLockAlreadyReportedToBatteryStats(tag, ownerUid)) {
+            try {
+                final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
+                if (workSource != null) {
+                    mBatteryStats.noteStartWakelockFromSource(
+                            workSource, ownerPid, tag, monitorType);
+                } else {
+                    mBatteryStats.noteStartWakelock(
+                            ownerUid, ownerPid, tag, monitorType);
+                }
+            } catch (RemoteException ex) {
+                // Ignore
+            }
+        }
+    }
+
+    /**
+     * Called when a wake lock is released.
+     */
+    public void onWakeLockReleased(int flags, String tag, int ownerUid, int ownerPid,
+            WorkSource workSource) {
+        if (DEBUG) {
+            Slog.d(TAG, "onWakeLockReleased: flags=" + flags + ", tag=\"" + tag
+                    + "\", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+                    + ", workSource=" + workSource);
+        }
+
+        if (!isWakeLockAlreadyReportedToBatteryStats(tag, ownerUid)) {
+            try {
+                final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
+                if (workSource != null) {
+                    mBatteryStats.noteStopWakelockFromSource(
+                            workSource, ownerPid, tag, monitorType);
+                } else {
+                    mBatteryStats.noteStopWakelock(
+                            ownerUid, ownerPid, tag, monitorType);
+                }
+            } catch (RemoteException ex) {
+                // Ignore
+            }
+        }
+    }
+
+    private static boolean isWakeLockAlreadyReportedToBatteryStats(String tag, int uid) {
+        // The window manager already takes care of reporting battery stats associated
+        // with the use of the KEEP_SCREEN_ON_FLAG.
+        // TODO: Use a WorkSource to handle this situation instead of hardcoding it here.
+        return uid == Process.SYSTEM_UID
+                && tag.equals(PowerManager.KEEP_SCREEN_ON_FLAG_TAG);
+    }
+
+    private static int getBatteryStatsWakeLockMonitorType(int flags) {
+        switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+            case PowerManager.PARTIAL_WAKE_LOCK:
+            case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
+                return BatteryStats.WAKE_TYPE_PARTIAL;
+            default:
+                return BatteryStats.WAKE_TYPE_FULL;
+        }
+    }
+
+    /**
+     * Called when the screen is turned on.
+     */
+    public void onScreenOn() {
+        if (DEBUG) {
+            Slog.d(TAG, "onScreenOn");
+        }
+
+        try {
+            mBatteryStats.noteScreenOn();
+        } catch (RemoteException ex) {
+            // Ignore
+        }
+    }
+
+    /**
+     * Called when the screen is turned off.
+     */
+    public void onScreenOff() {
+        if (DEBUG) {
+            Slog.d(TAG, "onScreenOff");
+        }
+
+        try {
+            mBatteryStats.noteScreenOff();
+        } catch (RemoteException ex) {
+            // Ignore
+        }
+    }
+
+    /**
+     * Called when the screen changes brightness.
+     */
+    public void onScreenBrightness(int brightness) {
+        if (DEBUG) {
+            Slog.d(TAG, "onScreenBrightness: brightness=" + brightness);
+        }
+
+        try {
+            mBatteryStats.noteScreenBrightness(brightness);
+        } catch (RemoteException ex) {
+            // Ignore
+        }
+    }
+
+    /**
+     * Called when the device is waking up from sleep and the
+     * display is about to be turned on.
+     */
+    public void onWakeUpStarted() {
+        if (DEBUG) {
+            Slog.d(TAG, "onWakeUpStarted");
+        }
+
+        synchronized (mLock) {
+            if (mActualPowerState != POWER_STATE_AWAKE) {
+                mActualPowerState = POWER_STATE_AWAKE;
+                updatePendingBroadcastLocked();
+            }
+        }
+    }
+
+    /**
+     * Called when the device has finished waking up from sleep
+     * and the display has been turned on.
+     */
+    public void onWakeUpFinished() {
+        if (DEBUG) {
+            Slog.d(TAG, "onWakeUpFinished");
+        }
+    }
+
+    /**
+     * Called when the device is going to sleep.
+     */
+    public void onGoToSleepStarted(int reason) {
+        if (DEBUG) {
+            Slog.d(TAG, "onGoToSleepStarted");
+        }
+
+        synchronized (mLock) {
+            mLastGoToSleepReason = reason;
+        }
+    }
+
+    /**
+     * Called when the device has finished going to sleep and the
+     * display has been turned off.
+     *
+     * This is a good time to make transitions that we don't want the user to see,
+     * such as bringing the key guard to focus.  There's no guarantee for this,
+     * however because the user could turn the device on again at any time.
+     * Some things may need to be protected by other mechanisms that defer screen on.
+     */
+    public void onGoToSleepFinished() {
+        if (DEBUG) {
+            Slog.d(TAG, "onGoToSleepFinished");
+        }
+
+        synchronized (mLock) {
+            if (mActualPowerState != POWER_STATE_ASLEEP) {
+                mActualPowerState = POWER_STATE_ASLEEP;
+                if (mUserActivityPending) {
+                    mUserActivityPending = false;
+                    mHandler.removeMessages(MSG_USER_ACTIVITY);
+                }
+                updatePendingBroadcastLocked();
+            }
+        }
+    }
+
+    /**
+     * Called when there has been user activity.
+     */
+    public void onUserActivity(int event, int uid) {
+        if (DEBUG) {
+            Slog.d(TAG, "onUserActivity: event=" + event + ", uid=" + uid);
+        }
+
+        try {
+            mBatteryStats.noteUserActivity(uid, event);
+        } catch (RemoteException ex) {
+            // Ignore
+        }
+
+        synchronized (mLock) {
+            if (!mUserActivityPending) {
+                mUserActivityPending = true;
+                Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY);
+                msg.setAsynchronous(true);
+                mHandler.sendMessage(msg);
+            }
+        }
+    }
+
+    private void updatePendingBroadcastLocked() {
+        if (!mBroadcastInProgress
+                && mActualPowerState != POWER_STATE_UNKNOWN
+                && mActualPowerState != mBroadcastedPowerState) {
+            mBroadcastInProgress = true;
+            mSuspendBlocker.acquire();
+            Message msg = mHandler.obtainMessage(MSG_BROADCAST);
+            msg.setAsynchronous(true);
+            mHandler.sendMessage(msg);
+        }
+    }
+
+    private void sendUserActivity() {
+        synchronized (mLock) {
+            if (!mUserActivityPending) {
+                return;
+            }
+            mUserActivityPending = false;
+        }
+
+        mPolicy.userActivity();
+    }
+
+    private void sendNextBroadcast() {
+        final int powerState;
+        final int goToSleepReason;
+        synchronized (mLock) {
+            if (mActualPowerState == POWER_STATE_UNKNOWN
+                    || mActualPowerState == mBroadcastedPowerState) {
+                mBroadcastInProgress = false;
+                mSuspendBlocker.release();
+                return;
+            }
+
+            powerState = mActualPowerState;
+            goToSleepReason = mLastGoToSleepReason;
+
+            mBroadcastedPowerState = powerState;
+            mBroadcastStartTime = SystemClock.uptimeMillis();
+        }
+
+        EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, 1);
+
+        if (powerState == POWER_STATE_AWAKE) {
+            sendWakeUpBroadcast();
+        } else {
+            sendGoToSleepBroadcast(goToSleepReason);
+        }
+    }
+
+    private void sendWakeUpBroadcast() {
+        if (DEBUG) {
+            Slog.d(TAG, "Sending wake up broadcast.");
+        }
+
+        EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);
+
+        mPolicy.screenTurningOn(mScreenOnListener);
+        try {
+            ActivityManagerNative.getDefault().wakingUp();
+        } catch (RemoteException e) {
+            // ignore it
+        }
+
+        if (ActivityManagerNative.isSystemReady()) {
+            mContext.sendOrderedBroadcast(mScreenOnIntent, null,
+                    mWakeUpBroadcastDone, mHandler, 0, null, null);
+        } else {
+            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1);
+            sendNextBroadcast();
+        }
+    }
+
+    private final BroadcastReceiver mWakeUpBroadcastDone = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 1,
+                    SystemClock.uptimeMillis() - mBroadcastStartTime, 1);
+            sendNextBroadcast();
+        }
+    };
+
+    private void sendGoToSleepBroadcast(int reason) {
+        if (DEBUG) {
+            Slog.d(TAG, "Sending go to sleep broadcast.");
+        }
+
+        int why = WindowManagerPolicy.OFF_BECAUSE_OF_USER;
+        switch (reason) {
+            case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
+                why = WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN;
+                break;
+            case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
+                why = WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT;
+                break;
+        }
+
+        EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, why, 0, 0);
+
+        mPolicy.screenTurnedOff(why);
+        try {
+            ActivityManagerNative.getDefault().goingToSleep();
+        } catch (RemoteException e) {
+            // ignore it.
+        }
+
+        if (ActivityManagerNative.isSystemReady()) {
+            mContext.sendOrderedBroadcast(mScreenOffIntent, null,
+                    mGoToSleepBroadcastDone, mHandler, 0, null, null);
+        } else {
+            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3, 1);
+            sendNextBroadcast();
+        }
+    }
+
+    private final BroadcastReceiver mGoToSleepBroadcastDone = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0,
+                    SystemClock.uptimeMillis() - mBroadcastStartTime, 1);
+            sendNextBroadcast();
+        }
+    };
+
+    private final class NotifierHandler extends Handler {
+        public NotifierHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_USER_ACTIVITY:
+                    sendUserActivity();
+                    break;
+
+                case MSG_BROADCAST:
+                    sendNextBroadcast();
+                    break;
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/power/PhotonicModulator.java b/services/java/com/android/server/power/PhotonicModulator.java
new file mode 100644
index 0000000..f7c9c7d
--- /dev/null
+++ b/services/java/com/android/server/power/PhotonicModulator.java
@@ -0,0 +1,87 @@
+/*
+ * 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;
+
+import com.android.server.LightsService;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Sets the value of a light asynchronously.
+ *
+ * This is done to avoid blocking the looper on devices for which
+ * setting the backlight brightness is especially slow.
+ */
+final class PhotonicModulator {
+    private static final int UNKNOWN_LIGHT_VALUE = -1;
+
+    private final Object mLock = new Object();
+
+    private final LightsService.Light mLight;
+    private final Executor mExecutor;
+    private final SuspendBlocker mSuspendBlocker;
+
+    private boolean mPendingChange;
+    private int mPendingLightValue;
+    private int mActualLightValue;
+
+    public PhotonicModulator(Executor executor, LightsService.Light light,
+            SuspendBlocker suspendBlocker) {
+        mExecutor = executor;
+        mLight = light;
+        mSuspendBlocker = suspendBlocker;
+        mPendingLightValue = UNKNOWN_LIGHT_VALUE;
+        mActualLightValue = UNKNOWN_LIGHT_VALUE;
+    }
+
+    /**
+     * Asynchronously sets the backlight brightness.
+     *
+     * @param lightValue The new light value, from 0 to 255.
+     */
+    public void setBrightness(int lightValue) {
+        synchronized (mLock) {
+            if (lightValue != mPendingLightValue) {
+                mPendingLightValue = lightValue;
+                if (!mPendingChange) {
+                    mPendingChange = true;
+                    mSuspendBlocker.acquire();
+                    mExecutor.execute(mTask);
+                }
+            }
+        }
+    }
+
+    private final Runnable mTask = new Runnable() {
+        @Override
+        public void run() {
+            for (;;) {
+                final int newLightValue;
+                synchronized (mLock) {
+                    newLightValue = mPendingLightValue;
+                    if (newLightValue == mActualLightValue) {
+                        mSuspendBlocker.release();
+                        mPendingChange = false;
+                        return;
+                    }
+                    mActualLightValue = newLightValue;
+                }
+                mLight.setBrightness(newLightValue);
+            }
+        }
+    };
+}
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index 2630239..2d91e6c 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -21,2767 +21,1432 @@
 import com.android.server.EventLogTags;
 import com.android.server.LightsService;
 import com.android.server.Watchdog;
-import com.android.server.am.BatteryStatsService;
+import com.android.server.am.ActivityManagerService;
 import com.android.server.display.DisplayManagerService;
 
-import android.app.ActivityManagerNative;
-import android.app.IActivityManager;
+import android.Manifest;
 import android.content.BroadcastReceiver;
-import android.content.ContentQueryMap;
 import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.database.ContentObserver;
-import android.database.Cursor;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.hardware.SystemSensorManager;
+import android.net.Uri;
 import android.os.BatteryManager;
-import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.IPowerManager;
-import android.os.LocalPowerManager;
+import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.WorkSource;
 import android.provider.Settings;
+import android.service.dreams.IDreamManager;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
+import android.util.TimeUtils;
 import android.view.WindowManagerPolicy;
-import static android.view.WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR;
-import static android.provider.Settings.System.DIM_SCREEN;
-import static android.provider.Settings.System.SCREEN_BRIGHTNESS;
-import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE;
-import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
-import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
-import static android.provider.Settings.System.STAY_ON_WHILE_PLUGGED_IN;
-import static android.provider.Settings.System.WINDOW_ANIMATION_SCALE;
-import static android.provider.Settings.System.TRANSITION_ANIMATION_SCALE;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Observable;
-import java.util.Observer;
 
-public class PowerManagerService extends IPowerManager.Stub
-        implements LocalPowerManager, Watchdog.Monitor {
-    private static final int NOMINAL_FRAME_TIME_MS = 1000/60;
+import libcore.util.Objects;
 
+/**
+ * The power manager service is responsible for coordinating power management
+ * functions on the device.
+ */
+public final class PowerManagerService extends IPowerManager.Stub
+        implements Watchdog.Monitor {
     private static final String TAG = "PowerManagerService";
-    static final String PARTIAL_NAME = "PowerManagerService";
 
-    // could be either static or controllable at runtime
     private static final boolean DEBUG = false;
-    private static final boolean DEBUG_PROXIMITY_SENSOR = (false || DEBUG);
-    private static final boolean DEBUG_LIGHT_SENSOR = (false || DEBUG);
-    private static final boolean DEBUG_LIGHT_ANIMATION = (false || DEBUG);
-    private static final boolean DEBUG_SCREEN_ON = false;
+    private static final boolean DEBUG_SPEW = DEBUG && true;
 
-    // Wake lock that ensures that the CPU is running.  The screen might not be on.
-    private static final int PARTIAL_WAKE_LOCK_ID = 1;
+    // Message: Sent when a user activity timeout occurs to update the power state.
+    private static final int MSG_USER_ACTIVITY_TIMEOUT = 1;
+    // Message: Sent when the device enters or exits a napping or dreaming state.
+    private static final int MSG_SANDMAN = 2;
 
-    // Wake lock that ensures that the screen is on.
-    private static final int FULL_WAKE_LOCK_ID = 2;
+    // Dirty bit: mWakeLocks changed
+    private static final int DIRTY_WAKE_LOCKS = 1 << 0;
+    // Dirty bit: mWakefulness changed
+    private static final int DIRTY_WAKEFULNESS = 1 << 1;
+    // Dirty bit: user activity was poked or may have timed out
+    private static final int DIRTY_USER_ACTIVITY = 1 << 2;
+    // Dirty bit: actual display power state was updated asynchronously
+    private static final int DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED = 1 << 3;
+    // Dirty bit: mBootCompleted changed
+    private static final int DIRTY_BOOT_COMPLETED = 1 << 4;
+    // Dirty bit: settings changed
+    private static final int DIRTY_SETTINGS = 1 << 5;
+    // Dirty bit: mIsPowered changed
+    private static final int DIRTY_IS_POWERED = 1 << 6;
+    // Dirty bit: mStayOn changed
+    private static final int DIRTY_STAY_ON = 1 << 7;
+    // Dirty bit: battery state changed
+    private static final int DIRTY_BATTERY_STATE = 1 << 8;
 
-    private static final boolean LOG_PARTIAL_WL = false;
+    // Wakefulness: The device is asleep and can only be awoken by a call to wakeUp().
+    // The screen should be off or in the process of being turned off by the display controller.
+    private static final int WAKEFULNESS_ASLEEP = 0;
+    // Wakefulness: The device is fully awake.  It can be put to sleep by a call to goToSleep().
+    // When the user activity timeout expires, the device may start napping.
+    private static final int WAKEFULNESS_AWAKE = 1;
+    // Wakefulness: The device is napping.  It is deciding whether to dream or go to sleep
+    // but hasn't gotten around to it yet.  It can be awoken by a call to wakeUp(), which
+    // ends the nap. User activity may brighten the screen but does not end the nap.
+    private static final int WAKEFULNESS_NAPPING = 2;
+    // Wakefulness: The device is dreaming.  It can be awoken by a call to wakeUp(),
+    // which ends the dream.  The device goes to sleep when goToSleep() is called, when
+    // the dream ends or when unplugged.
+    // User activity may brighten the screen but does not end the dream.
+    private static final int WAKEFULNESS_DREAMING = 3;
 
-    private static final int LOCK_MASK = PowerManager.PARTIAL_WAKE_LOCK
-                                        | PowerManager.SCREEN_DIM_WAKE_LOCK
-                                        | PowerManager.SCREEN_BRIGHT_WAKE_LOCK
-                                        | PowerManager.FULL_WAKE_LOCK
-                                        | PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK;
+    // Summarizes the state of all active wakelocks.
+    private static final int WAKE_LOCK_CPU = 1 << 0;
+    private static final int WAKE_LOCK_SCREEN_BRIGHT = 1 << 1;
+    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;
 
-    //                       time since last state:               time since last event:
-    // The short keylight delay comes from secure settings; this is the default.
-    private static final int SHORT_KEYLIGHT_DELAY_DEFAULT = 6000; // t+6 sec
-    private static final int MEDIUM_KEYLIGHT_DELAY = 15000;       // t+15 sec
-    private static final int LONG_KEYLIGHT_DELAY = 6000;        // t+6 sec
-    private static final int LONG_DIM_TIME = 7000;              // t+N-5 sec
+    // Summarizes the user activity state.
+    private static final int USER_ACTIVITY_SCREEN_BRIGHT = 1 << 0;
+    private static final int USER_ACTIVITY_SCREEN_DIM = 1 << 1;
 
-    // How long to wait to debounce light sensor changes in milliseconds
-    private static final int LIGHT_SENSOR_DELAY = 2000;
+    // Default and minimum screen off timeout in milliseconds.
+    private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15 * 1000;
+    private static final int MINIMUM_SCREEN_OFF_TIMEOUT = 10 * 1000;
 
-    // light sensor events rate in microseconds
-    private static final int LIGHT_SENSOR_RATE = 1000000;
+    // The screen dim duration, in seconds.
+    // This is subtracted from the end of the screen off timeout so the
+    // minimum screen off timeout should be longer than this.
+    private static final int SCREEN_DIM_DURATION = 7 * 1000;
 
-    // Expansion of range of light values when applying scale from light
-    // sensor brightness setting, in the [0..255] brightness range.
-    private static final int LIGHT_SENSOR_RANGE_EXPANSION = 20;
-
-    // Scaling factor of the light sensor brightness setting when applying
-    // it to the final brightness.
-    private static final int LIGHT_SENSOR_OFFSET_SCALE = 8;
-
-    // For debouncing the proximity sensor in milliseconds
-    private static final int PROXIMITY_SENSOR_DELAY = 1000;
-
-    // trigger proximity if distance is less than 5 cm
-    private static final float PROXIMITY_THRESHOLD = 5.0f;
-
-    // Cached secure settings; see updateSettingsValues()
-    private int mShortKeylightDelay = SHORT_KEYLIGHT_DELAY_DEFAULT;
-
-    // Default timeout for screen off, if not found in settings database = 15 seconds.
-    private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15000;
-
-    // Screen brightness should always have a value, but just in case...
-    private static final int DEFAULT_SCREEN_BRIGHTNESS = 192;
-
-    // Threshold for BRIGHTNESS_LOW_BATTERY (percentage)
-    // Screen will stay dim if battery level is <= LOW_BATTERY_THRESHOLD
-    private static final int LOW_BATTERY_THRESHOLD = 10;
-
-    // flags for setPowerState
-    private static final int ALL_LIGHTS_OFF         = 0x00000000;
-    private static final int SCREEN_ON_BIT          = 0x00000001;
-    private static final int SCREEN_BRIGHT_BIT      = 0x00000002;
-    private static final int BUTTON_BRIGHT_BIT      = 0x00000004;
-    private static final int KEYBOARD_BRIGHT_BIT    = 0x00000008;
-    private static final int BATTERY_LOW_BIT        = 0x00000010;
-
-    // values for setPowerState
-
-    // SCREEN_OFF == everything off
-    private static final int SCREEN_OFF         = 0x00000000;
-
-    // SCREEN_DIM == screen on, screen backlight dim
-    private static final int SCREEN_DIM         = SCREEN_ON_BIT;
-
-    // SCREEN_BRIGHT == screen on, screen backlight bright
-    private static final int SCREEN_BRIGHT      = SCREEN_ON_BIT | SCREEN_BRIGHT_BIT;
-
-    // SCREEN_BUTTON_BRIGHT == screen on, screen and button backlights bright
-    private static final int SCREEN_BUTTON_BRIGHT  = SCREEN_BRIGHT | BUTTON_BRIGHT_BIT;
-
-    // SCREEN_BUTTON_BRIGHT == screen on, screen, button and keyboard backlights bright
-    private static final int ALL_BRIGHT         = SCREEN_BUTTON_BRIGHT | KEYBOARD_BRIGHT_BIT;
-
-    // used for noChangeLights in setPowerState()
-    private static final int LIGHTS_MASK        = SCREEN_BRIGHT_BIT | BUTTON_BRIGHT_BIT | KEYBOARD_BRIGHT_BIT;
-
-    // animate screen lights in PowerManager (as opposed to SurfaceFlinger)
-    boolean mAnimateScreenLights = true;
-
-    static final int ANIM_STEPS = 60; // nominal # of frames at 60Hz
-    // Slower animation for autobrightness changes
-    static final int AUTOBRIGHTNESS_ANIM_STEPS = 2 * ANIM_STEPS;
-    // Even slower animation for autodimness changes. Set to max to effectively disable dimming.
-    // Note 100 is used to keep the mWindowScaleAnimation scaling below from overflowing an int.
-    static final int AUTODIMNESS_ANIM_STEPS = Integer.MAX_VALUE / (NOMINAL_FRAME_TIME_MS * 100);
-    // Number of steps when performing a more immediate brightness change.
-    static final int IMMEDIATE_ANIM_STEPS = 4;
-
-    // These magic numbers are the initial state of the LEDs at boot.  Ideally
-    // we should read them from the driver, but our current hardware returns 0
-    // for the initial value.  Oops!
-    static final int INITIAL_SCREEN_BRIGHTNESS = 255;
-    static final int INITIAL_BUTTON_BRIGHTNESS = PowerManager.BRIGHTNESS_OFF;
-    static final int INITIAL_KEYBOARD_BRIGHTNESS = PowerManager.BRIGHTNESS_OFF;
-
-    private final int MY_UID;
-    private final int MY_PID;
-
-    private boolean mDoneBooting = false;
-    private boolean mBootCompleted = false;
-    private boolean mHeadless = false;
-    private int mStayOnConditions = 0;
-    private final int[] mBroadcastQueue = new int[] { -1, -1, -1 };
-    private final int[] mBroadcastWhy = new int[3];
-    private boolean mPreparingForScreenOn = false;
-    private boolean mSkippedScreenOn = false;
-    private boolean mInitialized = false;
-    private int mPartialCount = 0;
-    private int mPowerState;
-    // mScreenOffReason can be WindowManagerPolicy.OFF_BECAUSE_OF_USER,
-    // WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT or WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR
-    private int mScreenOffReason;
-    private int mUserState;
-    private boolean mKeyboardVisible = false;
-    private boolean mUserActivityAllowed = true;
-    private int mProximityWakeLockCount = 0;
-    private boolean mProximitySensorEnabled = false;
-    private boolean mProximitySensorActive = false;
-    private int mProximityPendingValue = -1; // -1 == nothing, 0 == inactive, 1 == active
-    private long mLastProximityEventTime;
-    private int mScreenOffTimeoutSetting;
-    private int mMaximumScreenOffTimeout = Integer.MAX_VALUE;
-    private int mKeylightDelay;
-    private int mDimDelay;
-    private int mScreenOffDelay;
-    private int mWakeLockState;
-    private long mLastEventTime = 0;
-    private long mScreenOffTime;
-    private volatile WindowManagerPolicy mPolicy;
-    private final LockList mLocks = new LockList();
-    private Intent mScreenOffIntent;
-    private Intent mScreenOnIntent;
-    private LightsService mLightsService;
     private Context mContext;
-    private LightsService.Light mLcdLight;
-    private LightsService.Light mButtonLight;
-    private LightsService.Light mKeyboardLight;
-    private LightsService.Light mAttentionLight;
-    private UnsynchronizedWakeLock mBroadcastWakeLock;
-    private UnsynchronizedWakeLock mStayOnWhilePluggedInScreenDimLock;
-    private UnsynchronizedWakeLock mStayOnWhilePluggedInPartialLock;
-    private UnsynchronizedWakeLock mPreventScreenOnPartialLock;
-    private UnsynchronizedWakeLock mProximityPartialLock;
-    private HandlerThread mHandlerThread;
-    private Handler mScreenOffHandler;
-    private Handler mScreenBrightnessHandler;
-    private Handler mHandler;
-    private final TimeoutTask mTimeoutTask = new TimeoutTask();
-    private ScreenBrightnessAnimator mScreenBrightnessAnimator;
-    private boolean mWaitingForFirstLightSensor = false;
-    private boolean mStillNeedSleepNotification;
-    private boolean mIsPowered = false;
-    private IActivityManager mActivityService;
-    private IBatteryStats mBatteryStats;
+    private LightsService mLightsService;
     private BatteryService mBatteryService;
-    private DisplayManagerService mDisplayManagerService;
-    private SensorManager mSensorManager;
-    private Sensor mProximitySensor;
-    private Sensor mLightSensor;
-    private boolean mLightSensorEnabled;
-    private float mLightSensorValue = -1;
-    private boolean mProxIgnoredBecauseScreenTurnedOff = false;
-    private int mHighestLightSensorValue = -1;
-    private boolean mLightSensorPendingDecrease = false;
-    private boolean mLightSensorPendingIncrease = false;
-    private float mLightSensorPendingValue = -1;
-    private float mLightSensorAdjustSetting = 0;
-    private int mLightSensorScreenBrightness = -1;
-    private int mLightSensorButtonBrightness = -1;
-    private int mLightSensorKeyboardBrightness = -1;
-    private boolean mDimScreen = true;
-    private boolean mIsDocked = false;
-    private long mNextTimeout;
-    private volatile int mPokey = 0;
-    private volatile boolean mPokeAwakeOnSet = false;
-    private volatile boolean mInitComplete = false;
-    private final HashMap<IBinder,PokeLock> mPokeLocks = new HashMap<IBinder,PokeLock>();
-    // mLastScreenOnTime is the time the screen was last turned on
-    private long mLastScreenOnTime;
-    private boolean mPreventScreenOn;
-    private int mScreenBrightnessSetting = DEFAULT_SCREEN_BRIGHTNESS;
-    private int mScreenBrightnessOverride = -1;
-    private int mButtonBrightnessOverride = -1;
-    private int mScreenBrightnessDim;
-    private boolean mUseSoftwareAutoBrightness;
-    private boolean mAutoBrightessEnabled;
-    private int[] mAutoBrightnessLevels;
-    private int[] mLcdBacklightValues;
-    private int[] mButtonBacklightValues;
-    private int[] mKeyboardBacklightValues;
-    private int mLightSensorWarmupTime;
-    boolean mUnplugTurnsOnScreen;
-    private int mWarningSpewThrottleCount;
-    private long mWarningSpewThrottleTime;
-    private int mAnimationSetting = ANIM_SETTING_OFF;
-    private float mWindowScaleAnimation;
+    private IBatteryStats mBatteryStats;
+    private HandlerThread mHandlerThread;
+    private PowerManagerHandler mHandler;
+    private WindowManagerPolicy mPolicy;
+    private Notifier mNotifier;
+    private DisplayPowerController mDisplayPowerController;
+    private SettingsObserver mSettingsObserver;
+    private IDreamManager mDreamManager;
+    private LightsService.Light mAttentionLight;
 
-    // Must match with the ISurfaceComposer constants in C++.
-    private static final int ANIM_SETTING_ON = 0x01;
-    private static final int ANIM_SETTING_OFF = 0x10;
+    private final Object mLock = new Object();
+
+    // A bitfield that indicates what parts of the power state have
+    // changed and need to be recalculated.
+    private int mDirty;
+
+    // Indicates whether the device is awake or asleep or somewhere in between.
+    // This is distinct from the screen power state, which is managed separately.
+    private int mWakefulness;
+
+    // True if MSG_SANDMAN has been scheduled.
+    private boolean mSandmanScheduled;
+
+    // Table of all suspend blockers.
+    // There should only be a few of these.
+    private final ArrayList<SuspendBlocker> mSuspendBlockers = new ArrayList<SuspendBlocker>();
+
+    // Table of all wake locks acquired by applications.
+    private final ArrayList<WakeLock> mWakeLocks = new ArrayList<WakeLock>();
+
+    // A bitfield that summarizes the state of all active wakelocks.
+    private int mWakeLockSummary;
+
+    // If true, instructs the display controller to wait for the proximity sensor to
+    // go negative before turning the screen on.
+    private boolean mRequestWaitForNegativeProximity;
+
+    // Timestamp of the last time the device was awoken or put to sleep.
+    private long mLastWakeTime;
+    private long mLastSleepTime;
+
+    // True if we need to send a wake up or go to sleep finished notification
+    // when the display is ready.
+    private boolean mSendWakeUpFinishedNotificationWhenReady;
+    private boolean mSendGoToSleepFinishedNotificationWhenReady;
+
+    // Timestamp of the last call to user activity.
+    private long mLastUserActivityTime;
+    private long mLastUserActivityTimeNoChangeLights;
+
+    // A bitfield that summarizes the effect of the user activity timer.
+    // A zero value indicates that the user activity timer has expired.
+    private int mUserActivitySummary;
+
+    // The desired display power state.  The actual state may lag behind the
+    // requested because it is updated asynchronously by the display power controller.
+    private final DisplayPowerRequest mDisplayPowerRequest = new DisplayPowerRequest();
+
+    // The time the screen was last turned off, in elapsedRealtime() timebase.
+    private long mLastScreenOffEventElapsedRealTime;
+
+    // True if the display power state has been fully applied, which means the display
+    // is actually on or actually off or whatever was requested.
+    private boolean mDisplayReady;
+
+    // True if holding a wake-lock to block suspend of the CPU.
+    private boolean mHoldingWakeLockSuspendBlocker;
+
+    // The suspend blocker used to keep the CPU alive when wake locks have been acquired.
+    private final SuspendBlocker mWakeLockSuspendBlocker;
+
+    // True if systemReady() has been called.
+    private boolean mSystemReady;
+
+    // True if boot completed occurred.  We keep the screen on until this happens.
+    private boolean mBootCompleted;
+
+    // True if the device is plugged into a power source.
+    private boolean mIsPowered;
+
+    // True if the device should wake up when plugged or unplugged.
+    private boolean mWakeUpWhenPluggedOrUnpluggedConfig;
+
+    // True if dreams are supported on this device.
+    private boolean mDreamsSupportedConfig;
+
+    // True if dreams are enabled by the user.
+    private boolean mDreamsEnabledSetting;
+
+    // The screen off timeout setting value in milliseconds.
+    private int mScreenOffTimeoutSetting;
+
+    // The maximum allowable screen off timeout according to the device
+    // administration policy.  Overrides other settings.
+    private int mMaximumScreenOffTimeoutFromDeviceAdmin = Integer.MAX_VALUE;
+
+    // The stay on while plugged in setting.
+    // A bitfield of battery conditions under which to make the screen stay on.
+    private int mStayOnWhilePluggedInSetting;
+
+    // True if the device should stay on.
+    private boolean mStayOn;
+
+    // Screen brightness setting limits.
+    private int mScreenBrightnessSettingMinimum;
+    private int mScreenBrightnessSettingMaximum;
+    private int mScreenBrightnessSettingDefault;
+
+    // The screen brightness setting, from 0 to 255.
+    // Use -1 if no value has been set.
+    private int mScreenBrightnessSetting;
+
+    // The screen brightness mode.
+    // One of the Settings.System.SCREEN_BRIGHTNESS_MODE_* constants.
+    private int mScreenBrightnessModeSetting;
+
+    // The screen brightness setting override from the window manager
+    // to allow the current foreground activity to override the brightness.
+    // Use -1 to disable.
+    private int mScreenBrightnessOverrideFromWindowManager = -1;
+
+    // The screen brightness setting override from the settings application
+    // to temporarily adjust the brightness until next updated,
+    // Use -1 to disable.
+    private int mTemporaryScreenBrightnessSettingOverride = -1;
 
     private native void nativeInit();
-    private native void nativeSetPowerState(boolean screenOn, boolean screenBright);
-    private native void nativeStartSurfaceFlingerAnimation(int mode);
-    private static native void nativeAcquireWakeLock(int lock, String id);
-    private static native void nativeReleaseWakeLock(String id);
-    private static native int nativeSetScreenState(boolean on);
     private static native void nativeShutdown();
     private static native void nativeReboot(String reason) throws IOException;
 
-    /*
-    static PrintStream mLog;
-    static {
-        try {
-            mLog = new PrintStream("/data/power.log");
-        }
-        catch (FileNotFoundException e) {
-            android.util.Slog.e(TAG, "Life is hard", e);
-        }
-    }
-    static class Log {
-        static void d(String tag, String s) {
-            mLog.println(s);
-            android.util.Slog.d(tag, s);
-        }
-        static void i(String tag, String s) {
-            mLog.println(s);
-            android.util.Slog.i(tag, s);
-        }
-        static void w(String tag, String s) {
-            mLog.println(s);
-            android.util.Slog.w(tag, s);
-        }
-        static void e(String tag, String s) {
-            mLog.println(s);
-            android.util.Slog.e(tag, s);
-        }
-    }
-    */
+    private static native void nativeSetPowerState(boolean screenOn, boolean screenBright);
+    private static native void nativeAcquireSuspendBlocker(String name);
+    private static native void nativeReleaseSuspendBlocker(String name);
 
-    /**
-     * This class works around a deadlock between the lock in PowerManager.WakeLock
-     * and our synchronizing on mLocks.  PowerManager.WakeLock synchronizes on its
-     * mToken object so it can be accessed from any thread, but it calls into here
-     * with its lock held.  This class is essentially a reimplementation of
-     * PowerManager.WakeLock, but without that extra synchronized block, because we'll
-     * only call it with our own locks held.
-     */
-    private class UnsynchronizedWakeLock {
-        int mFlags;
-        String mTag;
-        IBinder mToken;
-        int mCount = 0;
-        boolean mRefCounted;
-        boolean mHeld;
-
-        UnsynchronizedWakeLock(int flags, String tag, boolean refCounted) {
-            mFlags = flags;
-            mTag = tag;
-            mToken = new Binder();
-            mRefCounted = refCounted;
-        }
-
-        public void acquire() {
-            if (!mRefCounted || mCount++ == 0) {
-                long ident = Binder.clearCallingIdentity();
-                try {
-                    PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken,
-                            MY_UID, MY_PID, mTag, null);
-                    mHeld = true;
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-            }
-        }
-
-        public void release() {
-            if (!mRefCounted || --mCount == 0) {
-                PowerManagerService.this.releaseWakeLockLocked(mToken, 0, false);
-                mHeld = false;
-            }
-            if (mCount < 0) {
-                throw new RuntimeException("WakeLock under-locked " + mTag);
-            }
-        }
-
-        public boolean isHeld()
-        {
-            return mHeld;
-        }
-
-        public String toString() {
-            return "UnsynchronizedWakeLock(mFlags=0x" + Integer.toHexString(mFlags)
-                    + " mCount=" + mCount + " mHeld=" + mHeld + ")";
-        }
-    }
-
-    private final class BatteryReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            synchronized (mLocks) {
-                boolean wasPowered = mIsPowered;
-                mIsPowered = mBatteryService.isPowered();
-
-                if (mIsPowered != wasPowered) {
-                    // update mStayOnWhilePluggedIn wake lock
-                    updateWakeLockLocked();
-
-                    // treat plugging and unplugging the devices as a user activity.
-                    // users find it disconcerting when they unplug the device
-                    // and it shuts off right away.
-                    // to avoid turning on the screen when unplugging, we only trigger
-                    // user activity when screen was already on.
-                    // temporarily set mUserActivityAllowed to true so this will work
-                    // even when the keyguard is on.
-                    // However, you can also set config_unplugTurnsOnScreen to have it
-                    // turn on.  Some devices want this because they don't have a
-                    // charging LED.
-                    synchronized (mLocks) {
-                        if (!wasPowered || (mPowerState & SCREEN_ON_BIT) != 0 ||
-                                mUnplugTurnsOnScreen) {
-                            forceUserActivityLocked();
-                        }
-                    }
-
-                    // stop the screensaver if we're now unplugged
-                    if (mPolicy != null && wasPowered) {
-                        mPolicy.stopScreenSaver();
-                    }
-                }
-            }
-        }
-    }
-
-    private final class BootCompletedReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            bootCompleted();
-        }
-    }
-
-    private final class DockReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
-                    Intent.EXTRA_DOCK_STATE_UNDOCKED);
-            dockStateChanged(state);
-        }
-    }
-
-    /**
-     * Set the setting that determines whether the device stays on when plugged in.
-     * The argument is a bit string, with each bit specifying a power source that,
-     * when the device is connected to that source, causes the device to stay on.
-     * See {@link android.os.BatteryManager} for the list of power sources that
-     * can be specified. Current values include {@link android.os.BatteryManager#BATTERY_PLUGGED_AC}
-     * and {@link android.os.BatteryManager#BATTERY_PLUGGED_USB}
-     * @param val an {@code int} containing the bits that specify which power sources
-     * should cause the device to stay on.
-     */
-    public void setStayOnSetting(int val) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SETTINGS, null);
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.STAY_ON_WHILE_PLUGGED_IN, val);
-    }
-
-    public void setMaximumScreenOffTimeount(int timeMs) {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.WRITE_SECURE_SETTINGS, null);
-        synchronized (mLocks) {
-            mMaximumScreenOffTimeout = timeMs;
-            // recalculate everything
-            setScreenOffTimeoutsLocked();
-        }
-    }
-
-    int getStayOnConditionsLocked() {
-        return mMaximumScreenOffTimeout <= 0 || mMaximumScreenOffTimeout == Integer.MAX_VALUE
-                ? mStayOnConditions : 0;
-    }
-
-    private class SettingsObserver implements Observer {
-        private int getInt(String name, int defValue) {
-            ContentValues values = mSettings.getValues(name);
-            Integer iVal = values != null ? values.getAsInteger(Settings.System.VALUE) : null;
-            return iVal != null ? iVal : defValue;
-        }
-
-        private float getFloat(String name, float defValue) {
-            ContentValues values = mSettings.getValues(name);
-            Float fVal = values != null ? values.getAsFloat(Settings.System.VALUE) : null;
-            return fVal != null ? fVal : defValue;
-        }
-
-        public void update(Observable o, Object arg) {
-            synchronized (mLocks) {
-                // STAY_ON_WHILE_PLUGGED_IN, default to when plugged into AC
-                mStayOnConditions = getInt(STAY_ON_WHILE_PLUGGED_IN,
-                        BatteryManager.BATTERY_PLUGGED_AC);
-                updateWakeLockLocked();
-
-                // SCREEN_OFF_TIMEOUT, default to 15 seconds
-                mScreenOffTimeoutSetting = getInt(SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT);
-
-                // DIM_SCREEN
-                //mDimScreen = getInt(DIM_SCREEN) != 0;
-
-                mScreenBrightnessSetting = getInt(SCREEN_BRIGHTNESS, DEFAULT_SCREEN_BRIGHTNESS);
-                mLightSensorAdjustSetting = 0; //getFloat(SCREEN_AUTO_BRIGHTNESS_ADJ, 0);
-
-                // SCREEN_BRIGHTNESS_MODE, default to manual
-                setScreenBrightnessMode(getInt(SCREEN_BRIGHTNESS_MODE,
-                        Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL));
-
-                // recalculate everything
-                setScreenOffTimeoutsLocked();
-
-                mWindowScaleAnimation = getFloat(WINDOW_ANIMATION_SCALE, 1.0f);
-                final float transitionScale = getFloat(TRANSITION_ANIMATION_SCALE, 1.0f);
-                mAnimationSetting = 0;
-                if (mWindowScaleAnimation > 0.5f) {
-                    mAnimationSetting |= ANIM_SETTING_OFF;
-                }
-                if (transitionScale > 0.5f) {
-                    // Uncomment this if you want the screen-on animation.
-                    // mAnimationSetting |= ANIM_SETTING_ON;
-                }
-            }
-        }
-    }
+    static native void nativeSetScreenState(boolean on);
 
     public PowerManagerService() {
-        // Hack to get our uid...  should have a func for this.
-        long token = Binder.clearCallingIdentity();
-        MY_UID = Process.myUid();
-        MY_PID = Process.myPid();
-        Binder.restoreCallingIdentity(token);
-
-        // assume nothing is on yet
-        mUserState = mPowerState = 0;
-
-        // Add ourself to the Watchdog monitors.
-        Watchdog.getInstance().addMonitor(this);
+        synchronized (mLock) {
+            mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService");
+            mWakeLockSuspendBlocker.acquire();
+            mHoldingWakeLockSuspendBlocker = true;
+            mWakefulness = WAKEFULNESS_AWAKE;
+        }
 
         nativeInit();
     }
 
-    private ContentQueryMap mSettings;
-
-    public void init(Context context, LightsService lights, IActivityManager activity,
-            BatteryService battery, DisplayManagerService displayManagerService) {
-        mLightsService = lights;
+    /**
+     * Initialize the power manager.
+     * Must be called before any other functions within the power manager are called.
+     */
+    public void init(Context context, LightsService ls,
+            ActivityManagerService am, BatteryService bs, IBatteryStats bss,
+            DisplayManagerService dm) {
         mContext = context;
-        mActivityService = activity;
-        mBatteryStats = BatteryStatsService.getService();
-        mBatteryService = battery;
-        mDisplayManagerService = displayManagerService;
-
-        mLcdLight = lights.getLight(LightsService.LIGHT_ID_BACKLIGHT);
-        mButtonLight = lights.getLight(LightsService.LIGHT_ID_BUTTONS);
-        mKeyboardLight = lights.getLight(LightsService.LIGHT_ID_KEYBOARD);
-        mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
-        mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
-
-        mInitComplete = false;
-        mScreenBrightnessAnimator = new ScreenBrightnessAnimator("mScreenBrightnessUpdaterThread",
-                Process.THREAD_PRIORITY_DISPLAY);
-        mScreenBrightnessAnimator.start();
-
-        synchronized (mScreenBrightnessAnimator) {
-            while (!mInitComplete) {
-                try {
-                    mScreenBrightnessAnimator.wait();
-                } catch (InterruptedException e) {
-                    // Ignore
-                }
-            }
-        }
-
-        mInitComplete = false;
-        mHandlerThread = new HandlerThread("PowerManagerService") {
-            @Override
-            protected void onLooperPrepared() {
-                super.onLooperPrepared();
-                initInThread();
-            }
-        };
+        mLightsService = ls;
+        mBatteryService = bs;
+        mBatteryStats = bss;
+        mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
+        mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
 
-        synchronized (mHandlerThread) {
-            while (!mInitComplete) {
-                try {
-                    mHandlerThread.wait();
-                } catch (InterruptedException e) {
-                    // Ignore
-                }
-            }
-        }
+        Watchdog.getInstance().addMonitor(this);
+    }
 
-        synchronized (mLocks) {
-            updateNativePowerStateLocked();
-            // We make sure to start out with the screen on due to user activity.
-            // (They did just boot their device, after all.)
-            forceUserActivityLocked();
-            mInitialized = true;
+    public void setPolicy(WindowManagerPolicy policy) {
+        synchronized (mLock) {
+            mPolicy = policy;
         }
     }
 
-    void initInThread() {
-        mHandler = new Handler();
+    public void systemReady() {
+        synchronized (mLock) {
+            mSystemReady = true;
 
-        mBroadcastWakeLock = new UnsynchronizedWakeLock(
-                                PowerManager.PARTIAL_WAKE_LOCK, "sleep_broadcast", true);
-        mStayOnWhilePluggedInScreenDimLock = new UnsynchronizedWakeLock(
-                                PowerManager.SCREEN_DIM_WAKE_LOCK, "StayOnWhilePluggedIn Screen Dim", false);
-        mStayOnWhilePluggedInPartialLock = new UnsynchronizedWakeLock(
-                                PowerManager.PARTIAL_WAKE_LOCK, "StayOnWhilePluggedIn Partial", false);
-        mPreventScreenOnPartialLock = new UnsynchronizedWakeLock(
-                                PowerManager.PARTIAL_WAKE_LOCK, "PreventScreenOn Partial", false);
-        mProximityPartialLock = new UnsynchronizedWakeLock(
-                                PowerManager.PARTIAL_WAKE_LOCK, "Proximity Partial", false);
+            PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+            mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting();
+            mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting();
+            mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting();
 
-        mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
-        mScreenOnIntent.addFlags(
-                Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
-        mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
-        mScreenOffIntent.addFlags(
-                Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
+            mNotifier = new Notifier(mHandler.getLooper(), mContext, mBatteryStats,
+                    createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
+                    mPolicy, mScreenOnListener);
+            mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(),
+                    mContext, mNotifier, mLightsService,
+                    createSuspendBlockerLocked("PowerManagerService.Display"),
+                    mDisplayPowerControllerCallbacks, mHandler);
 
-        Resources resources = mContext.getResources();
+            mSettingsObserver = new SettingsObserver(mHandler);
+            mAttentionLight = mLightsService.getLight(LightsService.LIGHT_ID_ATTENTION);
 
-        mAnimateScreenLights = resources.getBoolean(
-                com.android.internal.R.bool.config_animateScreenLights);
+            // Register for broadcasts from other components of the system.
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+            mContext.registerReceiver(new BatteryReceiver(), filter);
 
-        mUnplugTurnsOnScreen = resources.getBoolean(
+            filter = new IntentFilter();
+            filter.addAction(Intent.ACTION_BOOT_COMPLETED);
+            mContext.registerReceiver(new BootCompletedReceiver(), filter);
+
+            filter = new IntentFilter();
+            filter.addAction(Intent.ACTION_DOCK_EVENT);
+            mContext.registerReceiver(new DockReceiver(), filter);
+
+            // Register for settings changes.
+            final ContentResolver resolver = mContext.getContentResolver();
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.SCREENSAVER_ENABLED), false, mSettingsObserver);
+            resolver.registerContentObserver(Settings.System.getUriFor(
+                    Settings.System.SCREEN_OFF_TIMEOUT), false, mSettingsObserver);
+            resolver.registerContentObserver(Settings.System.getUriFor(
+                    Settings.System.STAY_ON_WHILE_PLUGGED_IN), false, mSettingsObserver);
+            resolver.registerContentObserver(Settings.System.getUriFor(
+                    Settings.System.SCREEN_BRIGHTNESS), false, mSettingsObserver);
+            resolver.registerContentObserver(Settings.System.getUriFor(
+                    Settings.System.SCREEN_BRIGHTNESS_MODE), false, mSettingsObserver);
+
+            // Go.
+            readConfigurationLocked();
+            updateSettingsLocked();
+            mDirty |= DIRTY_BATTERY_STATE;
+            updatePowerStateLocked();
+        }
+    }
+
+    private void readConfigurationLocked() {
+        final Resources resources = mContext.getResources();
+
+        mWakeUpWhenPluggedOrUnpluggedConfig = resources.getBoolean(
                 com.android.internal.R.bool.config_unplugTurnsOnScreen);
-
-        mScreenBrightnessDim = resources.getInteger(
-                com.android.internal.R.integer.config_screenBrightnessDim);
-
-        // read settings for auto-brightness
-        mUseSoftwareAutoBrightness = resources.getBoolean(
-                com.android.internal.R.bool.config_automatic_brightness_available);
-        if (mUseSoftwareAutoBrightness) {
-            mAutoBrightnessLevels = resources.getIntArray(
-                    com.android.internal.R.array.config_autoBrightnessLevels);
-            mLcdBacklightValues = resources.getIntArray(
-                    com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
-            mButtonBacklightValues = resources.getIntArray(
-                    com.android.internal.R.array.config_autoBrightnessButtonBacklightValues);
-            mKeyboardBacklightValues = resources.getIntArray(
-                    com.android.internal.R.array.config_autoBrightnessKeyboardBacklightValues);
-            mLightSensorWarmupTime = resources.getInteger(
-                    com.android.internal.R.integer.config_lightSensorWarmupTime);
-        }
-
-       ContentResolver resolver = mContext.getContentResolver();
-        Cursor settingsCursor = resolver.query(Settings.System.CONTENT_URI, null,
-                "(" + Settings.System.NAME + "=?) or ("
-                        + Settings.System.NAME + "=?) or ("
-                        + Settings.System.NAME + "=?) or ("
-                        + Settings.System.NAME + "=?) or ("
-                        + Settings.System.NAME + "=?) or ("
-                        + Settings.System.NAME + "=?) or ("
-                        + Settings.System.NAME + "=?) or ("
-                        + Settings.System.NAME + "=?)",
-                new String[]{STAY_ON_WHILE_PLUGGED_IN, SCREEN_OFF_TIMEOUT, DIM_SCREEN, SCREEN_BRIGHTNESS,
-                        SCREEN_BRIGHTNESS_MODE, /*SCREEN_AUTO_BRIGHTNESS_ADJ,*/
-                        WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE},
-                null);
-        mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mHandler);
-        SettingsObserver settingsObserver = new SettingsObserver();
-        mSettings.addObserver(settingsObserver);
-
-        // pretend that the settings changed so we will get their initial state
-        settingsObserver.update(mSettings, null);
-
-        // register for the battery changed notifications
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
-        mContext.registerReceiver(new BatteryReceiver(), filter);
-        filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_BOOT_COMPLETED);
-        mContext.registerReceiver(new BootCompletedReceiver(), filter);
-        filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_DOCK_EVENT);
-        mContext.registerReceiver(new DockReceiver(), filter);
-
-        // Listen for secure settings changes
-        mContext.getContentResolver().registerContentObserver(
-            Settings.Secure.CONTENT_URI, true,
-            new ContentObserver(new Handler()) {
-                public void onChange(boolean selfChange) {
-                    updateSettingsValues();
-                }
-            });
-        updateSettingsValues();
-
-        synchronized (mHandlerThread) {
-            mInitComplete = true;
-            mHandlerThread.notifyAll();
-        }
+        mDreamsSupportedConfig = resources.getBoolean(
+                com.android.internal.R.bool.config_enableDreams);
     }
 
-    /**
-     * Low-level function turn the device off immediately, without trying
-     * to be clean.  Most people should use
-     * {@link com.android.server.power.internal.app.ShutdownThread} for a clean shutdown.
-     */
-    public static void lowLevelShutdown() {
-        nativeShutdown();
-    }
+    private void updateSettingsLocked() {
+        final ContentResolver resolver = mContext.getContentResolver();
 
-    /**
-     * Low-level function to reboot the device.
-     *
-     * @param reason code to pass to the kernel (e.g. "recovery"), or null.
-     * @throws IOException if reboot fails for some reason (eg, lack of
-     *         permission)
-     */
-    public static void lowLevelReboot(String reason) throws IOException {
-        nativeReboot(reason);
-    }
+        mDreamsEnabledSetting = (Settings.Secure.getInt(resolver,
+                Settings.Secure.SCREENSAVER_ENABLED, 0) != 0);
+        mScreenOffTimeoutSetting = Settings.System.getInt(resolver,
+                Settings.System.SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT);
+        mStayOnWhilePluggedInSetting = Settings.System.getInt(resolver,
+                Settings.System.STAY_ON_WHILE_PLUGGED_IN,
+                BatteryManager.BATTERY_PLUGGED_AC);
 
-    private class WakeLock implements IBinder.DeathRecipient
-    {
-        WakeLock(int f, IBinder b, String t, int u, int p) {
-            super();
-            flags = f;
-            binder = b;
-            tag = t;
-            uid = u == MY_UID ? Process.SYSTEM_UID : u;
-            pid = p;
-            if (u != MY_UID || (
-                    !"KEEP_SCREEN_ON_FLAG".equals(tag)
-                    && !"KeyInputQueue".equals(tag))) {
-                monitorType = (f & LOCK_MASK) == PowerManager.PARTIAL_WAKE_LOCK
-                        ? BatteryStats.WAKE_TYPE_PARTIAL
-                        : BatteryStats.WAKE_TYPE_FULL;
-            } else {
-                monitorType = -1;
-            }
-            try {
-                b.linkToDeath(this, 0);
-            } catch (RemoteException e) {
-                binderDied();
-            }
+        final int oldScreenBrightnessSetting = mScreenBrightnessSetting;
+        mScreenBrightnessSetting = Settings.System.getInt(resolver,
+                Settings.System.SCREEN_BRIGHTNESS, mScreenBrightnessSettingDefault);
+        if (oldScreenBrightnessSetting != mScreenBrightnessSetting) {
+            mTemporaryScreenBrightnessSettingOverride = -1;
         }
-        public void binderDied() {
-            synchronized (mLocks) {
-                releaseWakeLockLocked(this.binder, 0, true);
-            }
-        }
-        final int flags;
-        final IBinder binder;
-        final String tag;
-        final int uid;
-        final int pid;
-        final int monitorType;
-        WorkSource ws;
-        boolean activated = true;
-        int minState;
+
+        mScreenBrightnessModeSetting = Settings.System.getInt(resolver,
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+
+        mDirty |= DIRTY_SETTINGS;
     }
 
-    private void updateWakeLockLocked() {
-        final int stayOnConditions = getStayOnConditionsLocked();
-        if (stayOnConditions != 0 && mBatteryService.isPowered(stayOnConditions)) {
-            // keep the device on if we're plugged in and mStayOnWhilePluggedIn is set.
-            mStayOnWhilePluggedInScreenDimLock.acquire();
-            mStayOnWhilePluggedInPartialLock.acquire();
+    private void handleSettingsChangedLocked() {
+        updateSettingsLocked();
+        updatePowerStateLocked();
+    }
+
+    @Override // Binder call
+    public void acquireWakeLock(IBinder lock, int flags, String tag, WorkSource ws) {
+        if (lock == null) {
+            throw new IllegalArgumentException("lock must not be null");
+        }
+        PowerManager.validateWakeLockParameters(flags, tag);
+
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
+        if (ws != null && ws.size() != 0) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.UPDATE_DEVICE_STATS, null);
         } else {
-            mStayOnWhilePluggedInScreenDimLock.release();
-            mStayOnWhilePluggedInPartialLock.release();
+            ws = null;
         }
-    }
 
-    private boolean isScreenLock(int flags)
-    {
-        int n = flags & LOCK_MASK;
-        return n == PowerManager.FULL_WAKE_LOCK
-                || n == PowerManager.SCREEN_BRIGHT_WAKE_LOCK
-                || n == PowerManager.SCREEN_DIM_WAKE_LOCK
-                || n == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK;
-    }
-
-    void enforceWakeSourcePermission(int uid, int pid) {
-        if (uid == Process.myUid()) {
-            return;
-        }
-        mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
-                pid, uid, null);
-    }
-
-    public void acquireWakeLock(int flags, IBinder lock, String tag, WorkSource ws) {
-        int uid = Binder.getCallingUid();
-        int pid = Binder.getCallingPid();
-        if (uid != Process.myUid()) {
-            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
-        }
-        if (ws != null) {
-            enforceWakeSourcePermission(uid, pid);
-        }
-        long ident = Binder.clearCallingIdentity();
+        final int uid = Binder.getCallingUid();
+        final int pid = Binder.getCallingPid();
+        final long ident = Binder.clearCallingIdentity();
         try {
-            synchronized (mLocks) {
-                acquireWakeLockLocked(flags, lock, uid, pid, tag, ws);
-            }
+            acquireWakeLockInternal(lock, flags, tag, ws, uid, pid);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
     }
 
-    void noteStartWakeLocked(WakeLock wl, WorkSource ws) {
-        if (wl.monitorType >= 0) {
-            long origId = Binder.clearCallingIdentity();
-            try {
-                if (ws != null) {
-                    mBatteryStats.noteStartWakelockFromSource(ws, wl.pid, wl.tag,
-                            wl.monitorType);
-                } else {
-                    mBatteryStats.noteStartWakelock(wl.uid, wl.pid, wl.tag, wl.monitorType);
-                }
-            } catch (RemoteException e) {
-                // Ignore
-            } finally {
-                Binder.restoreCallingIdentity(origId);
+    private void acquireWakeLockInternal(IBinder lock, int flags, String tag, WorkSource ws,
+            int uid, int pid) {
+        synchronized (mLock) {
+            if (DEBUG_SPEW) {
+                Slog.d(TAG, "acquireWakeLockInternal: lock=" + Objects.hashCode(lock)
+                        + ", flags=0x" + Integer.toHexString(flags)
+                        + ", tag=\"" + tag + "\", ws=" + ws + ", uid=" + uid + ", pid=" + pid);
             }
-        }
-    }
 
-    void noteStopWakeLocked(WakeLock wl, WorkSource ws) {
-        if (wl.monitorType >= 0) {
-            long origId = Binder.clearCallingIdentity();
-            try {
-                if (ws != null) {
-                    mBatteryStats.noteStopWakelockFromSource(ws, wl.pid, wl.tag,
-                            wl.monitorType);
-                } else {
-                    mBatteryStats.noteStopWakelock(wl.uid, wl.pid, wl.tag, wl.monitorType);
-                }
-            } catch (RemoteException e) {
-                // Ignore
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
-    public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag,
-            WorkSource ws) {
-        if (DEBUG) {
-            Slog.d(TAG, "acquireWakeLock flags=0x" + Integer.toHexString(flags) + " tag=" + tag);
-        }
-
-        if (ws != null && ws.size() == 0) {
-            ws = null;
-        }
-
-        int index = mLocks.getIndex(lock);
-        WakeLock wl;
-        boolean newlock;
-        boolean diffsource;
-        WorkSource oldsource;
-        if (index < 0) {
-            wl = new WakeLock(flags, lock, tag, uid, pid);
-            switch (wl.flags & LOCK_MASK)
-            {
-                case PowerManager.FULL_WAKE_LOCK:
-                    if (mUseSoftwareAutoBrightness) {
-                        wl.minState = SCREEN_BRIGHT;
-                    } else {
-                        wl.minState = (mKeyboardVisible ? ALL_BRIGHT : SCREEN_BUTTON_BRIGHT);
-                    }
-                    break;
-                case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
-                    wl.minState = SCREEN_BRIGHT;
-                    break;
-                case PowerManager.SCREEN_DIM_WAKE_LOCK:
-                    wl.minState = SCREEN_DIM;
-                    break;
-                case PowerManager.PARTIAL_WAKE_LOCK:
-                case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
-                    break;
-                default:
-                    // just log and bail.  we're in the server, so don't
-                    // throw an exception.
-                    Slog.e(TAG, "bad wakelock type for lock '" + tag + "' "
-                            + " flags=" + flags);
-                    return;
-            }
-            mLocks.addLock(wl);
-            if (ws != null) {
-                wl.ws = new WorkSource(ws);
-            }
-            newlock = true;
-            diffsource = false;
-            oldsource = null;
-        } else {
-            wl = mLocks.get(index);
-            newlock = false;
-            oldsource = wl.ws;
-            if (oldsource != null) {
-                if (ws == null) {
-                    wl.ws = null;
-                    diffsource = true;
-                } else {
-                    diffsource = oldsource.diff(ws);
-                }
-            } else if (ws != null) {
-                diffsource = true;
-            } else {
-                diffsource = false;
-            }
-            if (diffsource) {
-                wl.ws = new WorkSource(ws);
-            }
-        }
-        if (isScreenLock(flags)) {
-            // if this causes a wakeup, we reactivate all of the locks and
-            // set it to whatever they want.  otherwise, we modulate that
-            // by the current state so we never turn it more on than
-            // it already is.
-            if ((flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) {
-                mProximityWakeLockCount++;
-                if (mProximityWakeLockCount == 1) {
-                    enableProximityLockLocked();
+            WakeLock wakeLock;
+            int index = findWakeLockIndexLocked(lock);
+            if (index >= 0) {
+                wakeLock = mWakeLocks.get(index);
+                if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) {
+                    // Update existing wake lock.  This shouldn't happen but is harmless.
+                    notifyWakeLockReleasedLocked(wakeLock);
+                    wakeLock.updateProperties(flags, tag, ws, uid, pid);
+                    notifyWakeLockAcquiredLocked(wakeLock);
                 }
             } else {
-                if ((wl.flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {
-                    int oldWakeLockState = mWakeLockState;
-                    mWakeLockState = mLocks.reactivateScreenLocksLocked();
-
-                    // Disable proximity sensor if if user presses power key while we are in the
-                    // "waiting for proximity sensor to go negative" state.
-                    if ((mWakeLockState & SCREEN_ON_BIT) != 0
-                            && mProximitySensorActive && mProximityWakeLockCount == 0) {
-                        mProximitySensorActive = false;
-                    }
-
-                    if (DEBUG) {
-                        Slog.d(TAG, "wakeup here mUserState=0x" + Integer.toHexString(mUserState)
-                                + " mWakeLockState=0x"
-                                + Integer.toHexString(mWakeLockState)
-                                + " previous wakeLockState=0x"
-                                + Integer.toHexString(oldWakeLockState));
-                    }
-                } else {
-                    if (DEBUG) {
-                        Slog.d(TAG, "here mUserState=0x" + Integer.toHexString(mUserState)
-                                + " mLocks.gatherState()=0x"
-                                + Integer.toHexString(mLocks.gatherState())
-                                + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState));
-                    }
-                    mWakeLockState = (mUserState | mWakeLockState) & mLocks.gatherState();
+                wakeLock = new WakeLock(lock, flags, tag, ws, uid, pid);
+                try {
+                    lock.linkToDeath(wakeLock, 0);
+                } catch (RemoteException ex) {
+                    throw new IllegalArgumentException("Wake lock is already dead.");
                 }
-                setPowerState(mWakeLockState | mUserState);
+                notifyWakeLockAcquiredLocked(wakeLock);
+                mWakeLocks.add(wakeLock);
             }
-        }
-        else if ((flags & LOCK_MASK) == PowerManager.PARTIAL_WAKE_LOCK) {
-            if (newlock) {
-                mPartialCount++;
-                if (mPartialCount == 1) {
-                    if (LOG_PARTIAL_WL) {
-                        EventLog.writeEvent(EventLogTags.POWER_PARTIAL_WAKE_STATE, 1, tag);
-                    }
-                }
-            }
-            nativeAcquireWakeLock(PARTIAL_WAKE_LOCK_ID, PARTIAL_NAME);
-        }
 
-        if (diffsource) {
-            // If the lock sources have changed, need to first release the
-            // old ones.
-            noteStopWakeLocked(wl, oldsource);
-        }
-        if (newlock || diffsource) {
-            noteStartWakeLocked(wl, ws);
+            applyWakeLockFlagsOnAcquireLocked(wakeLock);
+            mDirty |= DIRTY_WAKE_LOCKS;
+            updatePowerStateLocked();
         }
     }
 
-    public void updateWakeLockWorkSource(IBinder lock, WorkSource ws) {
-        int uid = Binder.getCallingUid();
-        int pid = Binder.getCallingPid();
-        if (ws != null && ws.size() == 0) {
-            ws = null;
-        }
-        if (ws != null) {
-            enforceWakeSourcePermission(uid, pid);
-        }
-        synchronized (mLocks) {
-            int index = mLocks.getIndex(lock);
-            if (index < 0) {
-                throw new IllegalArgumentException("Wake lock not active");
-            }
-            WakeLock wl = mLocks.get(index);
-            WorkSource oldsource = wl.ws;
-            wl.ws = ws != null ? new WorkSource(ws) : null;
-            noteStopWakeLocked(wl, oldsource);
-            noteStartWakeLocked(wl, ws);
+    private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock) {
+        if ((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {
+            wakeUpNoUpdateLocked(SystemClock.uptimeMillis());
         }
     }
 
+    @Override // Binder call
     public void releaseWakeLock(IBinder lock, int flags) {
-        int uid = Binder.getCallingUid();
-        if (uid != Process.myUid()) {
-            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
+        if (lock == null) {
+            throw new IllegalArgumentException("lock must not be null");
         }
 
-        synchronized (mLocks) {
-            releaseWakeLockLocked(lock, flags, false);
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            releaseWakeLockInternal(lock, flags);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
-    private void releaseWakeLockLocked(IBinder lock, int flags, boolean death) {
-        WakeLock wl = mLocks.removeLock(lock);
-        if (wl == null) {
-            return;
-        }
-
-        if (DEBUG) {
-            Slog.d(TAG, "releaseWakeLock flags=0x"
-                    + Integer.toHexString(wl.flags) + " tag=" + wl.tag);
-        }
-
-        if (isScreenLock(wl.flags)) {
-            if ((wl.flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) {
-                mProximityWakeLockCount--;
-                if (mProximityWakeLockCount == 0) {
-                    if (mProximitySensorActive &&
-                            ((flags & PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE) != 0)) {
-                        // wait for proximity sensor to go negative before disabling sensor
-                        if (DEBUG_PROXIMITY_SENSOR) {
-                            Slog.d(TAG, "waiting for proximity sensor to go negative");
-                        }
-                    } else {
-                        disableProximityLockLocked();
-                    }
-                }
-            } else {
-                mWakeLockState = mLocks.gatherState();
-                // goes in the middle to reduce flicker
-                if ((wl.flags & PowerManager.ON_AFTER_RELEASE) != 0) {
-                    userActivity(SystemClock.uptimeMillis(), -1, false, PowerManager.USER_ACTIVITY_EVENT_OTHER, false, true);
-                }
-                setPowerState(mWakeLockState | mUserState);
-            }
-        }
-        else if ((wl.flags & LOCK_MASK) == PowerManager.PARTIAL_WAKE_LOCK) {
-            mPartialCount--;
-            if (mPartialCount == 0) {
-                if (LOG_PARTIAL_WL) {
-                    EventLog.writeEvent(EventLogTags.POWER_PARTIAL_WAKE_STATE, 0, wl.tag);
-                }
-                nativeReleaseWakeLock(PARTIAL_NAME);
-            }
-        }
-        // Unlink the lock from the binder.
-        wl.binder.unlinkToDeath(wl, 0);
-
-        noteStopWakeLocked(wl, wl.ws);
-    }
-
-    private class PokeLock implements IBinder.DeathRecipient
-    {
-        PokeLock(int p, IBinder b, String t) {
-            super();
-            this.pokey = p;
-            this.binder = b;
-            this.tag = t;
-            try {
-                b.linkToDeath(this, 0);
-            } catch (RemoteException e) {
-                binderDied();
-            }
-        }
-        public void binderDied() {
-            setPokeLock(0, this.binder, this.tag);
-        }
-        int pokey;
-        IBinder binder;
-        String tag;
-        boolean awakeOnSet;
-    }
-
-    public void setPokeLock(int pokey, IBinder token, String tag) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
-        if (token == null) {
-            Slog.e(TAG, "setPokeLock got null token for tag='" + tag + "'");
-            return;
-        }
-
-        if ((pokey & POKE_LOCK_TIMEOUT_MASK) == POKE_LOCK_TIMEOUT_MASK) {
-            throw new IllegalArgumentException("setPokeLock can't have both POKE_LOCK_SHORT_TIMEOUT"
-                    + " and POKE_LOCK_MEDIUM_TIMEOUT");
-        }
-
-        synchronized (mLocks) {
-            if (pokey != 0) {
-                PokeLock p = mPokeLocks.get(token);
-                int oldPokey = 0;
-                if (p != null) {
-                    oldPokey = p.pokey;
-                    p.pokey = pokey;
-                } else {
-                    p = new PokeLock(pokey, token, tag);
-                    mPokeLocks.put(token, p);
-                }
-                int oldTimeout = oldPokey & POKE_LOCK_TIMEOUT_MASK;
-                int newTimeout = pokey & POKE_LOCK_TIMEOUT_MASK;
-                if (((mPowerState & SCREEN_ON_BIT) == 0) && (oldTimeout != newTimeout)) {
-                    p.awakeOnSet = true;
-                }
-            } else {
-                PokeLock rLock = mPokeLocks.remove(token);
-                if (rLock != null) {
-                    token.unlinkToDeath(rLock, 0);
-                }
+    private void releaseWakeLockInternal(IBinder lock, int flags) {
+        synchronized (mLock) {
+            if (DEBUG_SPEW) {
+                Slog.d(TAG, "releaseWakeLockInternal: lock=" + Objects.hashCode(lock)
+                        + ", flags=0x" + Integer.toHexString(flags));
             }
 
-            int oldPokey = mPokey;
-            int cumulative = 0;
-            boolean awakeOnSet = false;
-            for (PokeLock p: mPokeLocks.values()) {
-                cumulative |= p.pokey;
-                if (p.awakeOnSet) {
-                    awakeOnSet = true;
-                }
-            }
-            mPokey = cumulative;
-            mPokeAwakeOnSet = awakeOnSet;
-
-            int oldCumulativeTimeout = oldPokey & POKE_LOCK_TIMEOUT_MASK;
-            int newCumulativeTimeout = pokey & POKE_LOCK_TIMEOUT_MASK;
-
-            if (oldCumulativeTimeout != newCumulativeTimeout) {
-                setScreenOffTimeoutsLocked();
-                // reset the countdown timer, but use the existing nextState so it doesn't
-                // change anything
-                setTimeoutLocked(SystemClock.uptimeMillis(), mTimeoutTask.nextState);
-            }
-        }
-    }
-
-    private static String lockType(int type)
-    {
-        switch (type)
-        {
-            case PowerManager.FULL_WAKE_LOCK:
-                return "FULL_WAKE_LOCK                ";
-            case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
-                return "SCREEN_BRIGHT_WAKE_LOCK       ";
-            case PowerManager.SCREEN_DIM_WAKE_LOCK:
-                return "SCREEN_DIM_WAKE_LOCK          ";
-            case PowerManager.PARTIAL_WAKE_LOCK:
-                return "PARTIAL_WAKE_LOCK             ";
-            case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
-                return "PROXIMITY_SCREEN_OFF_WAKE_LOCK";
-            default:
-                return "???                           ";
-        }
-    }
-
-    private static String dumpPowerState(int state) {
-        return (((state & KEYBOARD_BRIGHT_BIT) != 0)
-                        ? "KEYBOARD_BRIGHT_BIT " : "")
-                + (((state & SCREEN_BRIGHT_BIT) != 0)
-                        ? "SCREEN_BRIGHT_BIT " : "")
-                + (((state & SCREEN_ON_BIT) != 0)
-                        ? "SCREEN_ON_BIT " : "")
-                + (((state & BUTTON_BRIGHT_BIT) != 0)
-                        ? "BUTTON_BRIGHT_BIT " : "")
-                + (((state & BATTERY_LOW_BIT) != 0)
-                        ? "BATTERY_LOW_BIT " : "");
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
-                != PackageManager.PERMISSION_GRANTED) {
-            pw.println("Permission Denial: can't dump PowerManager from from pid="
-                    + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid());
-            return;
-        }
-
-        long now = SystemClock.uptimeMillis();
-
-        synchronized (mLocks) {
-            pw.println("Power Manager State:");
-            pw.println("  mIsPowered=" + mIsPowered
-                    + " mPowerState=" + mPowerState
-                    + " mScreenOffTime=" + (SystemClock.elapsedRealtime()-mScreenOffTime)
-                    + " ms");
-            pw.println("  mPartialCount=" + mPartialCount);
-            pw.println("  mWakeLockState=" + dumpPowerState(mWakeLockState));
-            pw.println("  mUserState=" + dumpPowerState(mUserState));
-            pw.println("  mPowerState=" + dumpPowerState(mPowerState));
-            pw.println("  mLocks.gather=" + dumpPowerState(mLocks.gatherState()));
-            pw.println("  mNextTimeout=" + mNextTimeout + " now=" + now
-                    + " " + ((mNextTimeout-now)/1000) + "s from now");
-            pw.println("  mDimScreen=" + mDimScreen
-                    + " mStayOnConditions=" + mStayOnConditions
-                    + " mPreparingForScreenOn=" + mPreparingForScreenOn
-                    + " mSkippedScreenOn=" + mSkippedScreenOn);
-            pw.println("  mScreenOffReason=" + mScreenOffReason
-                    + " mUserState=" + mUserState);
-            pw.println("  mBroadcastQueue={" + mBroadcastQueue[0] + ',' + mBroadcastQueue[1]
-                    + ',' + mBroadcastQueue[2] + "}");
-            pw.println("  mBroadcastWhy={" + mBroadcastWhy[0] + ',' + mBroadcastWhy[1]
-                    + ',' + mBroadcastWhy[2] + "}");
-            pw.println("  mPokey=" + mPokey + " mPokeAwakeonSet=" + mPokeAwakeOnSet);
-            pw.println("  mKeyboardVisible=" + mKeyboardVisible
-                    + " mUserActivityAllowed=" + mUserActivityAllowed);
-            pw.println("  mKeylightDelay=" + mKeylightDelay + " mDimDelay=" + mDimDelay
-                    + " mScreenOffDelay=" + mScreenOffDelay);
-            pw.println("  mPreventScreenOn=" + mPreventScreenOn
-                    + "  mScreenBrightnessOverride=" + mScreenBrightnessOverride
-                    + "  mButtonBrightnessOverride=" + mButtonBrightnessOverride);
-            pw.println("  mScreenOffTimeoutSetting=" + mScreenOffTimeoutSetting
-                    + " mMaximumScreenOffTimeout=" + mMaximumScreenOffTimeout);
-            pw.println("  mLastScreenOnTime=" + mLastScreenOnTime);
-            pw.println("  mBroadcastWakeLock=" + mBroadcastWakeLock);
-            pw.println("  mStayOnWhilePluggedInScreenDimLock=" + mStayOnWhilePluggedInScreenDimLock);
-            pw.println("  mStayOnWhilePluggedInPartialLock=" + mStayOnWhilePluggedInPartialLock);
-            pw.println("  mPreventScreenOnPartialLock=" + mPreventScreenOnPartialLock);
-            pw.println("  mProximityPartialLock=" + mProximityPartialLock);
-            pw.println("  mProximityWakeLockCount=" + mProximityWakeLockCount);
-            pw.println("  mProximitySensorEnabled=" + mProximitySensorEnabled);
-            pw.println("  mProximitySensorActive=" + mProximitySensorActive);
-            pw.println("  mProximityPendingValue=" + mProximityPendingValue);
-            pw.println("  mLastProximityEventTime=" + mLastProximityEventTime);
-            pw.println("  mLightSensorEnabled=" + mLightSensorEnabled
-                    + " mLightSensorAdjustSetting=" + mLightSensorAdjustSetting);
-            pw.println("  mLightSensorValue=" + mLightSensorValue
-                    + " mLightSensorPendingValue=" + mLightSensorPendingValue);
-            pw.println("  mHighestLightSensorValue=" + mHighestLightSensorValue
-                    + " mWaitingForFirstLightSensor=" + mWaitingForFirstLightSensor);
-            pw.println("  mLightSensorPendingDecrease=" + mLightSensorPendingDecrease
-                    + " mLightSensorPendingIncrease=" + mLightSensorPendingIncrease);
-            pw.println("  mLightSensorScreenBrightness=" + mLightSensorScreenBrightness
-                    + " mLightSensorButtonBrightness=" + mLightSensorButtonBrightness
-                    + " mLightSensorKeyboardBrightness=" + mLightSensorKeyboardBrightness);
-            pw.println("  mUseSoftwareAutoBrightness=" + mUseSoftwareAutoBrightness);
-            pw.println("  mAutoBrightessEnabled=" + mAutoBrightessEnabled);
-            mScreenBrightnessAnimator.dump(pw, "mScreenBrightnessAnimator: ");
-
-            int N = mLocks.size();
-            pw.println();
-            pw.println("mLocks.size=" + N + ":");
-            for (int i=0; i<N; i++) {
-                WakeLock wl = mLocks.get(i);
-                String type = lockType(wl.flags & LOCK_MASK);
-                String acquireCausesWakeup = "";
-                if ((wl.flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {
-                    acquireCausesWakeup = "ACQUIRE_CAUSES_WAKEUP ";
-                }
-                String activated = "";
-                if (wl.activated) {
-                   activated = " activated";
-                }
-                pw.println("  " + type + " '" + wl.tag + "'" + acquireCausesWakeup
-                        + activated + " (minState=" + wl.minState + ", uid=" + wl.uid
-                        + ", pid=" + wl.pid + ")");
-            }
-
-            pw.println();
-            pw.println("mPokeLocks.size=" + mPokeLocks.size() + ":");
-            for (PokeLock p: mPokeLocks.values()) {
-                pw.println("    poke lock '" + p.tag + "':"
-                        + ((p.pokey & POKE_LOCK_IGNORE_TOUCH_EVENTS) != 0
-                                ? " POKE_LOCK_IGNORE_TOUCH_EVENTS" : "")
-                        + ((p.pokey & POKE_LOCK_SHORT_TIMEOUT) != 0
-                                ? " POKE_LOCK_SHORT_TIMEOUT" : "")
-                        + ((p.pokey & POKE_LOCK_MEDIUM_TIMEOUT) != 0
-                                ? " POKE_LOCK_MEDIUM_TIMEOUT" : ""));
-            }
-
-            pw.println();
-        }
-    }
-
-    private void setTimeoutLocked(long now, int nextState) {
-        setTimeoutLocked(now, -1, nextState);
-    }
-
-    // If they gave a timeoutOverride it is the number of seconds
-    // to screen-off.  Figure out where in the countdown cycle we
-    // should jump to.
-    private void setTimeoutLocked(long now, final long originalTimeoutOverride, int nextState) {
-        long timeoutOverride = originalTimeoutOverride;
-        if (mBootCompleted) {
-            synchronized (mLocks) {
-                long when = 0;
-                if (timeoutOverride <= 0) {
-                    switch (nextState)
-                    {
-                        case SCREEN_BRIGHT:
-                            when = now + mKeylightDelay;
-                            break;
-                        case SCREEN_DIM:
-                            if (mDimDelay >= 0) {
-                                when = now + mDimDelay;
-                                break;
-                            } else {
-                                Slog.w(TAG, "mDimDelay=" + mDimDelay + " while trying to dim");
-                            }
-                       case SCREEN_OFF:
-                            synchronized (mLocks) {
-                                when = now + mScreenOffDelay;
-                            }
-                            break;
-                        default:
-                            when = now;
-                            break;
-                    }
-                } else {
-                    override: {
-                        if (timeoutOverride <= mScreenOffDelay) {
-                            when = now + timeoutOverride;
-                            nextState = SCREEN_OFF;
-                            break override;
-                        }
-                        timeoutOverride -= mScreenOffDelay;
-
-                        if (mDimDelay >= 0) {
-                             if (timeoutOverride <= mDimDelay) {
-                                when = now + timeoutOverride;
-                                nextState = SCREEN_DIM;
-                                break override;
-                            }
-                            timeoutOverride -= mDimDelay;
-                        }
-
-                        when = now + timeoutOverride;
-                        nextState = SCREEN_BRIGHT;
-                    }
-                }
-                if (DEBUG) {
-                    Slog.d(TAG, "setTimeoutLocked now=" + now
-                            + " timeoutOverride=" + timeoutOverride
-                            + " nextState=" + nextState + " when=" + when);
-                }
-
-                mHandler.removeCallbacks(mTimeoutTask);
-                mTimeoutTask.nextState = nextState;
-                mTimeoutTask.remainingTimeoutOverride = timeoutOverride > 0
-                        ? (originalTimeoutOverride - timeoutOverride)
-                        : -1;
-                mHandler.postAtTime(mTimeoutTask, when);
-                mNextTimeout = when; // for debugging
-            }
-        }
-    }
-
-    private void cancelTimerLocked()
-    {
-        mHandler.removeCallbacks(mTimeoutTask);
-        mTimeoutTask.nextState = -1;
-    }
-
-    private class TimeoutTask implements Runnable
-    {
-        int nextState; // access should be synchronized on mLocks
-        long remainingTimeoutOverride;
-        public void run()
-        {
-            synchronized (mLocks) {
-                if (DEBUG) {
-                    Slog.d(TAG, "user activity timeout timed out nextState=" + this.nextState);
-                }
-
-                if (nextState == -1) {
-                    return;
-                }
-
-                mUserState = this.nextState;
-                setPowerState(this.nextState | mWakeLockState);
-
-                long now = SystemClock.uptimeMillis();
-
-                switch (this.nextState)
-                {
-                    case SCREEN_BRIGHT:
-                        if (mDimDelay >= 0) {
-                            setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_DIM);
-                            break;
-                        }
-                    case SCREEN_DIM:
-                        setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_OFF);
-                        break;
-                }
-            }
-        }
-    }
-
-    private void sendNotificationLocked(boolean on, int why) {
-        if (!mInitialized) {
-            // No notifications sent until first initialization is done.
-            // This is so that when we are moving from our initial state
-            // which looks like the screen was off to it being on, we do not
-            // go through the process of waiting for the higher-level user
-            // space to be ready before turning up the display brightness.
-            // (And also do not send needless broadcasts about the screen.)
-            return;
-        }
-
-        if (DEBUG_SCREEN_ON) {
-            RuntimeException here = new RuntimeException("here");
-            here.fillInStackTrace();
-            Slog.i(TAG, "sendNotificationLocked: " + on, here);
-        }
-
-        if (!on) {
-            mStillNeedSleepNotification = false;
-        }
-
-        // Add to the queue.
-        int index = 0;
-        while (mBroadcastQueue[index] != -1) {
-            index++;
-        }
-        mBroadcastQueue[index] = on ? 1 : 0;
-        mBroadcastWhy[index] = why;
-
-        // If we added it position 2, then there is a pair that can be stripped.
-        // If we added it position 1 and we're turning the screen off, we can strip
-        // the pair and do nothing, because the screen is already off, and therefore
-        // keyguard has already been enabled.
-        // However, if we added it at position 1 and we're turning it on, then position
-        // 0 was to turn it off, and we can't strip that, because keyguard needs to come
-        // on, so have to run the queue then.
-        if (index == 2) {
-            // While we're collapsing them, if it's going off, and the new reason
-            // is more significant than the first, then use the new one.
-            if (!on && mBroadcastWhy[0] > why) {
-                mBroadcastWhy[0] = why;
-            }
-            mBroadcastQueue[0] = on ? 1 : 0;
-            mBroadcastQueue[1] = -1;
-            mBroadcastQueue[2] = -1;
-            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 1, mBroadcastWakeLock.mCount);
-            mBroadcastWakeLock.release();
-            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 1, mBroadcastWakeLock.mCount);
-            mBroadcastWakeLock.release();
-            index = 0;
-        }
-        if (index == 1 && !on) {
-            mBroadcastQueue[0] = -1;
-            mBroadcastQueue[1] = -1;
-            index = -1;
-            // The wake lock was being held, but we're not actually going to do any
-            // broadcasts, so release the wake lock.
-            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 1, mBroadcastWakeLock.mCount);
-            mBroadcastWakeLock.release();
-        }
-
-        // The broadcast queue has changed; make sure the screen is on if it
-        // is now possible for it to be.
-        if (mSkippedScreenOn) {
-            updateLightsLocked(mPowerState, SCREEN_ON_BIT);
-        }
-
-        // Now send the message.
-        if (index >= 0) {
-            // Acquire the broadcast wake lock before changing the power
-            // state. It will be release after the broadcast is sent.
-            // We always increment the ref count for each notification in the queue
-            // and always decrement when that notification is handled.
-            mBroadcastWakeLock.acquire();
-            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, mBroadcastWakeLock.mCount);
-            mHandler.post(mNotificationTask);
-        }
-    }
-
-    private WindowManagerPolicy.ScreenOnListener mScreenOnListener =
-            new WindowManagerPolicy.ScreenOnListener() {
-                public void onScreenOn() {
-                    synchronized (mLocks) {
-                        if (mPreparingForScreenOn) {
-                            mPreparingForScreenOn = false;
-                            updateLightsLocked(mPowerState, SCREEN_ON_BIT);
-                            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP,
-                                    4, mBroadcastWakeLock.mCount);
-                            mBroadcastWakeLock.release();
-                        }
-                    }
-                }
-    };
-
-    private Runnable mNotificationTask = new Runnable()
-    {
-        public void run()
-        {
-            while (true) {
-                int value;
-                int why;
-                WindowManagerPolicy policy;
-                synchronized (mLocks) {
-                    value = mBroadcastQueue[0];
-                    why = mBroadcastWhy[0];
-                    for (int i=0; i<2; i++) {
-                        mBroadcastQueue[i] = mBroadcastQueue[i+1];
-                        mBroadcastWhy[i] = mBroadcastWhy[i+1];
-                    }
-                    policy = getPolicyLocked();
-                    if (value == 1 && !mPreparingForScreenOn) {
-                        mPreparingForScreenOn = true;
-                        mBroadcastWakeLock.acquire();
-                        EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND,
-                                mBroadcastWakeLock.mCount);
-                    }
-                }
-                if (value == 1) {
-                    mScreenOnStart = SystemClock.uptimeMillis();
-
-                    policy.screenTurningOn(mScreenOnListener);
-                    try {
-                        ActivityManagerNative.getDefault().wakingUp();
-                    } catch (RemoteException e) {
-                        // ignore it
-                    }
-
-                    if (DEBUG) {
-                        Slog.d(TAG, "mBroadcastWakeLock=" + mBroadcastWakeLock);
-                    }
-                    if (mContext != null && ActivityManagerNative.isSystemReady()) {
-                        mContext.sendOrderedBroadcast(mScreenOnIntent, null,
-                                mScreenOnBroadcastDone, mHandler, 0, null, null);
-                    } else {
-                        synchronized (mLocks) {
-                            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2,
-                                    mBroadcastWakeLock.mCount);
-                            mBroadcastWakeLock.release();
-                        }
-                    }
-                }
-                else if (value == 0) {
-                    mScreenOffStart = SystemClock.uptimeMillis();
-
-                    policy.screenTurnedOff(why);
-                    try {
-                        ActivityManagerNative.getDefault().goingToSleep();
-                    } catch (RemoteException e) {
-                        // ignore it.
-                    }
-
-                    if (mContext != null && ActivityManagerNative.isSystemReady()) {
-                        mContext.sendOrderedBroadcast(mScreenOffIntent, null,
-                                mScreenOffBroadcastDone, mHandler, 0, null, null);
-                    } else {
-                        synchronized (mLocks) {
-                            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3,
-                                    mBroadcastWakeLock.mCount);
-                            updateLightsLocked(mPowerState, SCREEN_ON_BIT);
-                            mBroadcastWakeLock.release();
-                        }
-                    }
-                }
-                else {
-                    // If we're in this case, then this handler is running for a previous
-                    // paired transaction.  mBroadcastWakeLock will already have been released.
-                    break;
-                }
-            }
-        }
-    };
-
-    long mScreenOnStart;
-    private BroadcastReceiver mScreenOnBroadcastDone = new BroadcastReceiver() {
-        public void onReceive(Context context, Intent intent) {
-            synchronized (mLocks) {
-                EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 1,
-                        SystemClock.uptimeMillis() - mScreenOnStart, mBroadcastWakeLock.mCount);
-                mBroadcastWakeLock.release();
-            }
-        }
-    };
-
-    long mScreenOffStart;
-    private BroadcastReceiver mScreenOffBroadcastDone = new BroadcastReceiver() {
-        public void onReceive(Context context, Intent intent) {
-            synchronized (mLocks) {
-                EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0,
-                        SystemClock.uptimeMillis() - mScreenOffStart, mBroadcastWakeLock.mCount);
-                mBroadcastWakeLock.release();
-            }
-        }
-    };
-
-    /**
-     * Prevents the screen from turning on even if it *should* turn on due
-     * to a subsequent full wake lock being acquired.
-     * <p>
-     * This is a temporary hack that allows an activity to "cover up" any
-     * display glitches that happen during the activity's startup
-     * sequence.  (Specifically, this API was added to work around a
-     * cosmetic bug in the "incoming call" sequence, where the lock screen
-     * would flicker briefly before the incoming call UI became visible.)
-     * TODO: There ought to be a more elegant way of doing this,
-     * probably by having the PowerManager and ActivityManager
-     * work together to let apps specify that the screen on/off
-     * state should be synchronized with the Activity lifecycle.
-     * <p>
-     * Note that calling preventScreenOn(true) will NOT turn the screen
-     * off if it's currently on.  (This API only affects *future*
-     * acquisitions of full wake locks.)
-     * But calling preventScreenOn(false) WILL turn the screen on if
-     * it's currently off because of a prior preventScreenOn(true) call.
-     * <p>
-     * Any call to preventScreenOn(true) MUST be followed promptly by a call
-     * to preventScreenOn(false).  In fact, if the preventScreenOn(false)
-     * call doesn't occur within 5 seconds, we'll turn the screen back on
-     * ourselves (and log a warning about it); this prevents a buggy app
-     * from disabling the screen forever.)
-     * <p>
-     * TODO: this feature should really be controlled by a new type of poke
-     * lock (rather than an IPowerManager call).
-     */
-    public void preventScreenOn(boolean prevent) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
-
-        synchronized (mLocks) {
-            if (prevent) {
-                // First of all, grab a partial wake lock to
-                // make sure the CPU stays on during the entire
-                // preventScreenOn(true) -> preventScreenOn(false) sequence.
-                mPreventScreenOnPartialLock.acquire();
-
-                // Post a forceReenableScreen() call (for 5 seconds in the
-                // future) to make sure the matching preventScreenOn(false) call
-                // has happened by then.
-                mHandler.removeCallbacks(mForceReenableScreenTask);
-                mHandler.postDelayed(mForceReenableScreenTask, 5000);
-
-                // Finally, set the flag that prevents the screen from turning on.
-                // (Below, in setPowerState(), we'll check mPreventScreenOn and
-                // we *won't* call setScreenStateLocked(true) if it's set.)
-                mPreventScreenOn = true;
-            } else {
-                // (Re)enable the screen.
-                mPreventScreenOn = false;
-
-                // We're "undoing" a the prior preventScreenOn(true) call, so we
-                // no longer need the 5-second safeguard.
-                mHandler.removeCallbacks(mForceReenableScreenTask);
-
-                // Forcibly turn on the screen if it's supposed to be on.  (This
-                // handles the case where the screen is currently off because of
-                // a prior preventScreenOn(true) call.)
-                if (!mProximitySensorActive && (mPowerState & SCREEN_ON_BIT) != 0) {
-                    if (DEBUG) {
-                        Slog.d(TAG,
-                              "preventScreenOn: turning on after a prior preventScreenOn(true)!");
-                    }
-                    int err = setScreenStateLocked(true);
-                    if (err != 0) {
-                        Slog.w(TAG, "preventScreenOn: error from setScreenStateLocked(): " + err);
-                    }
-                }
-
-                // Release the partial wake lock that we held during the
-                // preventScreenOn(true) -> preventScreenOn(false) sequence.
-                mPreventScreenOnPartialLock.release();
-            }
-        }
-    }
-
-    public void setScreenBrightnessOverride(int brightness) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
-
-        if (DEBUG) Slog.d(TAG, "setScreenBrightnessOverride " + brightness);
-        synchronized (mLocks) {
-            if (mScreenBrightnessOverride != brightness) {
-                mScreenBrightnessOverride = brightness;
-                if (isScreenOn()) {
-                    updateLightsLocked(mPowerState, SCREEN_ON_BIT);
-                }
-            }
-        }
-    }
-
-    public void setButtonBrightnessOverride(int brightness) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
-
-        if (DEBUG) Slog.d(TAG, "setButtonBrightnessOverride " + brightness);
-         synchronized (mLocks) {
-           if (mButtonBrightnessOverride != brightness) {
-                mButtonBrightnessOverride = brightness;
-                if (isScreenOn()) {
-                    updateLightsLocked(mPowerState, BUTTON_BRIGHT_BIT | KEYBOARD_BRIGHT_BIT);
-                }
-            }
-        }
-    }
-
-    /**
-     * Sanity-check that gets called 5 seconds after any call to
-     * preventScreenOn(true).  This ensures that the original call
-     * is followed promptly by a call to preventScreenOn(false).
-     */
-    private void forceReenableScreen() {
-        // We shouldn't get here at all if mPreventScreenOn is false, since
-        // we should have already removed any existing
-        // mForceReenableScreenTask messages...
-        if (!mPreventScreenOn) {
-            Slog.w(TAG, "forceReenableScreen: mPreventScreenOn is false, nothing to do");
-            return;
-        }
-
-        // Uh oh.  It's been 5 seconds since a call to
-        // preventScreenOn(true) and we haven't re-enabled the screen yet.
-        // This means the app that called preventScreenOn(true) is either
-        // slow (i.e. it took more than 5 seconds to call preventScreenOn(false)),
-        // or buggy (i.e. it forgot to call preventScreenOn(false), or
-        // crashed before doing so.)
-
-        // Log a warning, and forcibly turn the screen back on.
-        Slog.w(TAG, "App called preventScreenOn(true) but didn't promptly reenable the screen! "
-              + "Forcing the screen back on...");
-        preventScreenOn(false);
-    }
-
-    private Runnable mForceReenableScreenTask = new Runnable() {
-            public void run() {
-                forceReenableScreen();
-            }
-        };
-
-    private int setScreenStateLocked(boolean on) {
-        if (DEBUG_SCREEN_ON) {
-            RuntimeException e = new RuntimeException("here");
-            e.fillInStackTrace();
-            Slog.i(TAG, "Set screen state: " + on, e);
-        }
-        if (on) {
-            if (mInitialized && ((mPowerState & SCREEN_ON_BIT) == 0 || mSkippedScreenOn)) {
-                // If we are turning the screen state on, but the screen
-                // light is currently off, then make sure that we set the
-                // light at this point to 0.  This is the case where we are
-                // turning on the screen and waiting for the UI to be drawn
-                // before showing it to the user.  We want the light off
-                // until it is ready to be shown to the user, not it using
-                // whatever the last value it had.
-                // Skip this if the screen is being turned on for the first time
-                // after boot (mInitialized is false).
-                if (DEBUG_SCREEN_ON) {
-                    Slog.i(TAG, "Forcing brightness 0: mPowerState=0x"
-                            + Integer.toHexString(mPowerState)
-                            + " mSkippedScreenOn=" + mSkippedScreenOn);
-                }
-                mScreenBrightnessAnimator.animateTo(PowerManager.BRIGHTNESS_OFF, SCREEN_BRIGHT_BIT, 0);
-            }
-        }
-        int err = nativeSetScreenState(on);
-        if (err == 0) {
-            mLastScreenOnTime = (on ? SystemClock.elapsedRealtime() : 0);
-            if (mUseSoftwareAutoBrightness) {
-                enableLightSensorLocked(on);
-                if (on) {
-                    // If AutoBrightness is enabled, set the brightness immediately after the
-                    // next sensor value is received.
-                    mWaitingForFirstLightSensor = mAutoBrightessEnabled;
-                } else {
-                    // make sure button and key backlights are off too
-                    mButtonLight.turnOff();
-                    mKeyboardLight.turnOff();
-                }
-            }
-        }
-        return err;
-    }
-
-    private void setPowerState(int state)
-    {
-        setPowerState(state, false, WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT);
-    }
-
-    private void setPowerState(int newState, boolean noChangeLights, int reason)
-    {
-        synchronized (mLocks) {
-            int err;
-
-            if (DEBUG) {
-                Slog.d(TAG, "setPowerState: mPowerState=0x" + Integer.toHexString(mPowerState)
-                        + " newState=0x" + Integer.toHexString(newState)
-                        + " noChangeLights=" + noChangeLights
-                        + " reason=" + reason);
-            }
-            
-            if (noChangeLights) {
-                newState = (newState & ~LIGHTS_MASK) | (mPowerState & LIGHTS_MASK);
-            }
-            if (mProximitySensorActive) {
-                // don't turn on the screen when the proximity sensor lock is held
-                newState = (newState & ~SCREEN_BRIGHT);
-            }
-
-            if (batteryIsLow()) {
-                newState |= BATTERY_LOW_BIT;
-            } else {
-                newState &= ~BATTERY_LOW_BIT;
-            }
-            if (newState == mPowerState && mInitialized) {
+            int index = findWakeLockIndexLocked(lock);
+            if (index < 0) {
                 return;
             }
 
-            if (!mBootCompleted && !mUseSoftwareAutoBrightness) {
-                newState |= ALL_BRIGHT;
+            WakeLock wakeLock = mWakeLocks.get(index);
+            mWakeLocks.remove(index);
+            notifyWakeLockReleasedLocked(wakeLock);
+            wakeLock.mLock.unlinkToDeath(wakeLock, 0);
+
+            if ((flags & PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE) != 0) {
+                mRequestWaitForNegativeProximity = true;
             }
 
-            boolean oldScreenOn = (mPowerState & SCREEN_ON_BIT) != 0;
-            boolean newScreenOn = (newState & SCREEN_ON_BIT) != 0;
-
-            if (DEBUG) {
-                Slog.d(TAG, "setPowerState: mPowerState=" + mPowerState
-                        + " newState=" + newState + " noChangeLights=" + noChangeLights);
-                Slog.d(TAG, "  oldKeyboardBright=" + ((mPowerState & KEYBOARD_BRIGHT_BIT) != 0)
-                         + " newKeyboardBright=" + ((newState & KEYBOARD_BRIGHT_BIT) != 0));
-                Slog.d(TAG, "  oldScreenBright=" + ((mPowerState & SCREEN_BRIGHT_BIT) != 0)
-                         + " newScreenBright=" + ((newState & SCREEN_BRIGHT_BIT) != 0));
-                Slog.d(TAG, "  oldButtonBright=" + ((mPowerState & BUTTON_BRIGHT_BIT) != 0)
-                         + " newButtonBright=" + ((newState & BUTTON_BRIGHT_BIT) != 0));
-                Slog.d(TAG, "  oldScreenOn=" + oldScreenOn
-                         + " newScreenOn=" + newScreenOn);
-                Slog.d(TAG, "  oldBatteryLow=" + ((mPowerState & BATTERY_LOW_BIT) != 0)
-                         + " newBatteryLow=" + ((newState & BATTERY_LOW_BIT) != 0));
-            }
-
-            final boolean stateChanged = mPowerState != newState;
-
-            if (stateChanged && !newScreenOn && reason == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT) {
-                if (mPolicy != null && mIsPowered && mPolicy.isScreenSaverEnabled()) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "setPowerState: running screen saver instead of turning off screen");
-                    }
-                    if (mPolicy.startScreenSaver()) {
-                        // was successful
-                        return;
-                    }
-                }
-            }
-
-
-            if (oldScreenOn != newScreenOn) {
-                if (newScreenOn) {
-                    // When the user presses the power button, we need to always send out the
-                    // notification that it's going to sleep so the keyguard goes on.  But
-                    // we can't do that until the screen fades out, so we don't show the keyguard
-                    // too early.
-                    if (mStillNeedSleepNotification) {
-                        sendNotificationLocked(false, WindowManagerPolicy.OFF_BECAUSE_OF_USER);
-                    }
-
-                    // Turn on the screen UNLESS there was a prior
-                    // preventScreenOn(true) request.  (Note that the lifetime
-                    // of a single preventScreenOn() request is limited to 5
-                    // seconds to prevent a buggy app from disabling the
-                    // screen forever; see forceReenableScreen().)
-                    boolean reallyTurnScreenOn = true;
-                    if (DEBUG) {
-                        Slog.d(TAG, "- turning screen on...  mPreventScreenOn = "
-                              + mPreventScreenOn);
-                    }
-
-                    if (mPreventScreenOn) {
-                        if (DEBUG) {
-                            Slog.d(TAG, "- PREVENTING screen from really turning on!");
-                        }
-                        reallyTurnScreenOn = false;
-                    }
-                    if (reallyTurnScreenOn) {
-                        err = setScreenStateLocked(true);
-                        long identity = Binder.clearCallingIdentity();
-                        try {
-                            mBatteryStats.noteScreenBrightness(getPreferredBrightness());
-                            mBatteryStats.noteScreenOn();
-                        } catch (RemoteException e) {
-                            Slog.w(TAG, "RemoteException calling noteScreenOn on BatteryStatsService", e);
-                        } finally {
-                            Binder.restoreCallingIdentity(identity);
-                        }
-                    } else {
-                        setScreenStateLocked(false);
-                        // But continue as if we really did turn the screen on...
-                        err = 0;
-                    }
-
-                    EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, reason, 0, 0);
-                    if (err == 0) {
-                        sendNotificationLocked(true, -1);
-                        // Update the lights *after* taking care of turning the
-                        // screen on, so we do this after our notifications are
-                        // enqueued and thus will delay turning on the screen light
-                        // until the windows are correctly displayed.
-                        if (stateChanged) {
-                            updateLightsLocked(newState, 0);
-                        }
-                        mPowerState |= SCREEN_ON_BIT;
-                    }
-
-                } else {
-                    // Update the lights *before* taking care of turning the
-                    // screen off, so we can initiate any animations that are desired.
-                    mScreenOffReason = reason;
-                    if (stateChanged) {
-                        updateLightsLocked(newState, 0);
-                    }
-
-                    // cancel light sensor task
-                    mHandler.removeCallbacks(mAutoBrightnessTask);
-                    mLightSensorPendingDecrease = false;
-                    mLightSensorPendingIncrease = false;
-                    mScreenOffTime = SystemClock.elapsedRealtime();
-                    long identity = Binder.clearCallingIdentity();
-                    try {
-                        mBatteryStats.noteScreenOff();
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "RemoteException calling noteScreenOff on BatteryStatsService", e);
-                    } finally {
-                        Binder.restoreCallingIdentity(identity);
-                    }
-                    mPowerState &= ~SCREEN_ON_BIT;
-                    if (!mScreenBrightnessAnimator.isAnimating()) {
-                        err = screenOffFinishedAnimatingLocked(reason);
-                    } else {
-                        err = 0;
-                    }
-
-                    // stop the screensaver if user turned screen off
-                    if (stateChanged && reason == WindowManagerPolicy.OFF_BECAUSE_OF_USER) {
-                        if (mPolicy != null) {
-                            mPolicy.stopScreenSaver();
-                        }
-                    }
-                }
-            } else if (stateChanged) {
-                // Screen on/off didn't change, but lights may have.
-                updateLightsLocked(newState, 0);
-            }
-
-            mPowerState = (mPowerState & ~LIGHTS_MASK) | (newState & LIGHTS_MASK);
-
-            updateNativePowerStateLocked();
+            applyWakeLockFlagsOnReleaseLocked(wakeLock);
+            mDirty |= DIRTY_WAKE_LOCKS;
+            updatePowerStateLocked();
         }
     }
 
-    private void updateNativePowerStateLocked() {
-        if (!mHeadless) {
-            nativeSetPowerState(
-                    (mPowerState & SCREEN_ON_BIT) != 0,
-                    (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT);
+    private void handleWakeLockDeath(WakeLock wakeLock) {
+        synchronized (mLock) {
+            if (DEBUG_SPEW) {
+                Slog.d(TAG, "handleWakeLockDeath: lock=" + Objects.hashCode(wakeLock.mLock));
+            }
+
+            int index = mWakeLocks.indexOf(wakeLock);
+            if (index < 0) {
+                return;
+            }
+
+            mWakeLocks.remove(index);
+            notifyWakeLockReleasedLocked(wakeLock);
+
+            applyWakeLockFlagsOnReleaseLocked(wakeLock);
+            mDirty |= DIRTY_WAKE_LOCKS;
+            updatePowerStateLocked();
         }
     }
 
-    private int screenOffFinishedAnimatingLocked(int reason) {
-        // I don't think we need to check the current state here because all of these
-        // Power.setScreenState and sendNotificationLocked can both handle being
-        // called multiple times in the same state. -joeo
-        EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, reason, 0, 0);
-        int err = setScreenStateLocked(false);
-        if (err == 0) {
-            mScreenOffReason = reason;
-            sendNotificationLocked(false, reason);
+    private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) {
+        if ((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) != 0) {
+            userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
+                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
+                    PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS,
+                    wakeLock.mOwnerUid);
         }
-        return err;
     }
 
-    private boolean batteryIsLow() {
-        return (!mIsPowered &&
-                mBatteryService.getBatteryLevel() <= LOW_BATTERY_THRESHOLD);
-    }
+    @Override // Binder call
+    public void updateWakeLockWorkSource(IBinder lock, WorkSource ws) {
+        if (lock == null) {
+            throw new IllegalArgumentException("lock must not be null");
+        }
 
-    private boolean shouldDeferScreenOnLocked() {
-        if (mPreparingForScreenOn) {
-            // Currently waiting for confirmation from the policy that it
-            // is okay to turn on the screen.  Don't allow the screen to go
-            // on until that is done.
-            if (DEBUG_SCREEN_ON) Slog.i(TAG,
-                    "updateLights: delaying screen on due to mPreparingForScreenOn");
-            return true;
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
+        if (ws != null && ws.size() != 0) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.UPDATE_DEVICE_STATS, null);
         } else {
-            // If there is a screen-on command in the notification queue, we
-            // can't turn the screen on until it has been processed (and we
-            // have set mPreparingForScreenOn) or it has been dropped.
-            for (int i=0; i<mBroadcastQueue.length; i++) {
-                if (mBroadcastQueue[i] == 1) {
-                    if (DEBUG_SCREEN_ON) Slog.i(TAG,
-                            "updateLights: delaying screen on due to notification queue");
+            ws = null;
+        }
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            updateWakeLockWorkSourceInternal(lock, ws);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private void updateWakeLockWorkSourceInternal(IBinder lock, WorkSource ws) {
+        synchronized (mLock) {
+            int index = findWakeLockIndexLocked(lock);
+            if (index < 0) {
+                throw new IllegalArgumentException("Wake lock not active");
+            }
+
+            WakeLock wakeLock = mWakeLocks.get(index);
+            if (!wakeLock.hasSameWorkSource(ws)) {
+                notifyWakeLockReleasedLocked(wakeLock);
+                wakeLock.updateWorkSource(ws);
+                notifyWakeLockAcquiredLocked(wakeLock);
+            }
+        }
+    }
+
+    private int findWakeLockIndexLocked(IBinder lock) {
+        final int count = mWakeLocks.size();
+        for (int i = 0; i < count; i++) {
+            if (mWakeLocks.get(i).mLock == lock) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    private void notifyWakeLockAcquiredLocked(WakeLock wakeLock) {
+        if (mSystemReady) {
+            mNotifier.onWakeLockAcquired(wakeLock.mFlags, wakeLock.mTag,
+                    wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource);
+        }
+    }
+
+    private void notifyWakeLockReleasedLocked(WakeLock wakeLock) {
+        if (mSystemReady) {
+            mNotifier.onWakeLockReleased(wakeLock.mFlags, wakeLock.mTag,
+                    wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource);
+        }
+    }
+
+    @Override // Binder call
+    public boolean isWakeLockLevelSupported(int level) {
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            return isWakeLockLevelSupportedInternal(level);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private boolean isWakeLockLevelSupportedInternal(int level) {
+        synchronized (mLock) {
+            switch (level) {
+                case PowerManager.PARTIAL_WAKE_LOCK:
+                case PowerManager.SCREEN_DIM_WAKE_LOCK:
+                case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
+                case PowerManager.FULL_WAKE_LOCK:
                     return true;
-                }
+
+                case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
+                    return mSystemReady && mDisplayPowerController.isProximitySensorAvailable();
+
+                default:
+                    return false;
+            }
+        }
+    }
+
+    @Override // Binder call
+    public void userActivity(long eventTime, int event, int flags) {
+        if (eventTime > SystemClock.uptimeMillis()) {
+            throw new IllegalArgumentException("event time must not be in the future");
+        }
+
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+        final int uid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            userActivityInternal(eventTime, event, flags, uid);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    // Called from native code.
+    private void userActivityFromNative(long eventTime, int event, int flags) {
+        userActivityInternal(eventTime, event, flags, Process.SYSTEM_UID);
+    }
+
+    private void userActivityInternal(long eventTime, int event, int flags, int uid) {
+        synchronized (mLock) {
+            if (userActivityNoUpdateLocked(eventTime, event, flags, uid)) {
+                updatePowerStateLocked();
+            }
+        }
+    }
+
+    private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) {
+        if (DEBUG_SPEW) {
+            Slog.d(TAG, "userActivityNoUpdateLocked: eventTime=" + eventTime
+                    + ", event=" + event + ", flags=0x" + Integer.toHexString(flags)
+                    + ", uid=" + uid);
+        }
+
+        if (eventTime < mLastSleepTime || eventTime < mLastWakeTime
+                || mWakefulness == WAKEFULNESS_ASLEEP || !mBootCompleted || !mSystemReady) {
+            return false;
+        }
+
+        mNotifier.onUserActivity(event, uid);
+
+        if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) {
+            if (eventTime > mLastUserActivityTimeNoChangeLights
+                    && eventTime > mLastUserActivityTime) {
+                mLastUserActivityTimeNoChangeLights = eventTime;
+                mDirty |= DIRTY_USER_ACTIVITY;
+                return true;
+            }
+        } else {
+            if (eventTime > mLastUserActivityTime) {
+                mLastUserActivityTime = eventTime;
+                mDirty |= DIRTY_USER_ACTIVITY;
+                return true;
             }
         }
         return false;
     }
 
-    private void updateLightsLocked(int newState, int forceState) {
-        final int oldState = mPowerState;
+    @Override // Binder call
+    public void wakeUp(long eventTime) {
+        if (eventTime > SystemClock.uptimeMillis()) {
+            throw new IllegalArgumentException("event time must not be in the future");
+        }
 
-        // If the screen is not currently on, we will want to delay actually
-        // turning the lights on if we are still getting the UI put up.
-        if ((oldState & SCREEN_ON_BIT) == 0 || mSkippedScreenOn) {
-            // Don't turn screen on until we know we are really ready to.
-            // This is to avoid letting the screen go on before things like the
-            // lock screen have been displayed.
-            if ((mSkippedScreenOn = shouldDeferScreenOnLocked())) {
-                newState &= ~(SCREEN_ON_BIT|SCREEN_BRIGHT_BIT);
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            wakeUpInternal(eventTime);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    // Called from native code.
+    private void wakeUpFromNative(long eventTime) {
+        wakeUpInternal(eventTime);
+    }
+
+    private void wakeUpInternal(long eventTime) {
+        synchronized (mLock) {
+            if (wakeUpNoUpdateLocked(eventTime)) {
+                updatePowerStateLocked();
             }
         }
+    }
 
-        if ((newState & SCREEN_ON_BIT) != 0) {
-            // Only turn on the buttons or keyboard if the screen is also on.
-            // We should never see the buttons on but not the screen.
-            newState = applyButtonState(newState);
-            newState = applyKeyboardState(newState);
+    private boolean wakeUpNoUpdateLocked(long eventTime) {
+        if (DEBUG_SPEW) {
+            Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime);
         }
-        final int realDifference = (newState ^ oldState);
-        final int difference = realDifference | forceState;
-        if (difference == 0) {
+
+        if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE
+                || !mBootCompleted || !mSystemReady) {
+            return false;
+        }
+
+        switch (mWakefulness) {
+            case WAKEFULNESS_ASLEEP:
+                Slog.i(TAG, "Waking up from sleep...");
+                mNotifier.onWakeUpStarted();
+                mSendWakeUpFinishedNotificationWhenReady = true;
+                mSendGoToSleepFinishedNotificationWhenReady = false;
+                break;
+            case WAKEFULNESS_DREAMING:
+                Slog.i(TAG, "Waking up from dream...");
+                break;
+            case WAKEFULNESS_NAPPING:
+                Slog.i(TAG, "Waking up from nap...");
+                break;
+        }
+
+        mLastWakeTime = eventTime;
+        mWakefulness = WAKEFULNESS_AWAKE;
+        mDirty |= DIRTY_WAKEFULNESS;
+
+        userActivityNoUpdateLocked(
+                eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
+        return true;
+    }
+
+    @Override // Binder call
+    public void goToSleep(long eventTime, int reason) {
+        if (eventTime > SystemClock.uptimeMillis()) {
+            throw new IllegalArgumentException("event time must not be in the future");
+        }
+
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            goToSleepInternal(eventTime, reason);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    // Called from native code.
+    private void goToSleepFromNative(long eventTime, int reason) {
+        goToSleepInternal(eventTime, reason);
+    }
+
+    private void goToSleepInternal(long eventTime, int reason) {
+        synchronized (mLock) {
+            if (goToSleepNoUpdateLocked(eventTime, reason)) {
+                updatePowerStateLocked();
+            }
+        }
+    }
+
+    private boolean goToSleepNoUpdateLocked(long eventTime, int reason) {
+        if (DEBUG_SPEW) {
+            Slog.d(TAG, "goToSleepNoUpdateLocked: eventTime=" + eventTime + ", reason=" + reason);
+        }
+
+        if (eventTime < mLastWakeTime || mWakefulness == WAKEFULNESS_ASLEEP
+                || !mBootCompleted || !mSystemReady) {
+            return false;
+        }
+
+        switch (reason) {
+            case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
+                Slog.i(TAG, "Going to sleep due to device administration policy...");
+                break;
+            case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
+                Slog.i(TAG, "Going to sleep due to screen timeout...");
+                break;
+            default:
+                Slog.i(TAG, "Going to sleep by user request...");
+                reason = PowerManager.GO_TO_SLEEP_REASON_USER;
+                break;
+        }
+
+        mLastSleepTime = eventTime;
+        mDirty |= DIRTY_WAKEFULNESS;
+        mWakefulness = WAKEFULNESS_ASLEEP;
+        mNotifier.onGoToSleepStarted(reason);
+        mSendGoToSleepFinishedNotificationWhenReady = true;
+        mSendWakeUpFinishedNotificationWhenReady = false;
+
+        // Report the number of wake locks that will be cleared by going to sleep.
+        int numWakeLocksCleared = 0;
+        final int numWakeLocks = mWakeLocks.size();
+        for (int i = 0; i < numWakeLocks; i++) {
+            final WakeLock wakeLock = mWakeLocks.get(i);
+            switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+                case PowerManager.FULL_WAKE_LOCK:
+                case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
+                case PowerManager.SCREEN_DIM_WAKE_LOCK:
+                    numWakeLocksCleared += 1;
+                    break;
+            }
+        }
+        EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numWakeLocksCleared);
+        return true;
+    }
+
+    /**
+     * Updates the global power state based on dirty bits recorded in mDirty.
+     *
+     * This is the main function that performs power state transitions.
+     * We centralize them here so that we can recompute the power state completely
+     * each time something important changes, and ensure that we do it the same
+     * way each time.  The point is to gather all of the transition logic here.
+     */
+    private void updatePowerStateLocked() {
+        if (!mSystemReady || mDirty == 0) {
             return;
         }
 
-        int offMask = 0;
-        int dimMask = 0;
-        int onMask = 0;
+        // Phase 0: Basic state updates.
+        updateIsPoweredLocked(mDirty);
+        updateStayOnLocked(mDirty);
 
-        int preferredBrightness = getPreferredBrightness();
+        // Phase 1: Update wakefulness.
+        // Loop because the wake lock and user activity computations are influenced
+        // by changes in wakefulness.
+        final long now = SystemClock.uptimeMillis();
+        int dirtyPhase2 = 0;
+        for (;;) {
+            int dirtyPhase1 = mDirty;
+            dirtyPhase2 |= dirtyPhase1;
+            mDirty = 0;
 
-        if ((difference & KEYBOARD_BRIGHT_BIT) != 0) {
-            if ((newState & KEYBOARD_BRIGHT_BIT) == 0) {
-                offMask |= KEYBOARD_BRIGHT_BIT;
-            } else {
-                onMask |= KEYBOARD_BRIGHT_BIT;
+            updateWakeLockSummaryLocked(dirtyPhase1);
+            updateUserActivitySummaryLocked(now, dirtyPhase1);
+            if (!updateWakefulnessLocked(dirtyPhase1)) {
+                break;
             }
         }
 
-        if ((difference & BUTTON_BRIGHT_BIT) != 0) {
-            if ((newState & BUTTON_BRIGHT_BIT) == 0) {
-                offMask |= BUTTON_BRIGHT_BIT;
-            } else {
-                onMask |= BUTTON_BRIGHT_BIT;
-            }
-        }
+        // Phase 2: Update dreams and display power state.
+        updateDreamLocked(dirtyPhase2);
+        updateDisplayPowerStateLocked(dirtyPhase2);
 
-        if ((difference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) {
-            int nominalCurrentValue = -1;
-            // If there was an actual difference in the light state, then
-            // figure out the "ideal" current value based on the previous
-            // state.  Otherwise, this is a change due to the brightness
-            // override, so we want to animate from whatever the current
-            // value is.
-            if ((realDifference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) {
-                switch (oldState & (SCREEN_BRIGHT_BIT|SCREEN_ON_BIT)) {
-                    case SCREEN_BRIGHT_BIT | SCREEN_ON_BIT:
-                        nominalCurrentValue = preferredBrightness;
-                        break;
-                    case SCREEN_ON_BIT:
-                        nominalCurrentValue = mScreenBrightnessDim;
-                        break;
-                    case 0:
-                        nominalCurrentValue = PowerManager.BRIGHTNESS_OFF;
-                        break;
-                    case SCREEN_BRIGHT_BIT:
-                    default:
-                        // not possible
-                        nominalCurrentValue = (int)mScreenBrightnessAnimator.getCurrentBrightness();
-                        break;
-                }
-            }
-            int brightness = preferredBrightness;
-            int steps = ANIM_STEPS;
-            if ((newState & SCREEN_BRIGHT_BIT) == 0) {
-                // dim or turn off backlight, depending on if the screen is on
-                // the scale is because the brightness ramp isn't linear and this biases
-                // it so the later parts take longer.
-                final float scale = 1.5f;
-                float ratio = (((float)mScreenBrightnessDim)/preferredBrightness);
-                if (ratio > 1.0f) ratio = 1.0f;
-                if ((newState & SCREEN_ON_BIT) == 0) {
-                    if ((oldState & SCREEN_BRIGHT_BIT) != 0) {
-                        // was bright
-                        steps = ANIM_STEPS;
-                    } else {
-                        // was dim
-                        steps = (int)(ANIM_STEPS*ratio*scale);
-                    }
-                    brightness = PowerManager.BRIGHTNESS_OFF;
-                } else {
-                    if ((oldState & SCREEN_ON_BIT) != 0) {
-                        // was bright
-                        steps = (int)(ANIM_STEPS*(1.0f-ratio)*scale);
-                    } else {
-                        // was dim
-                        steps = (int)(ANIM_STEPS*ratio);
-                    }
-                    final int stayOnConditions = getStayOnConditionsLocked();
-                    if (stayOnConditions != 0 && mBatteryService.isPowered(stayOnConditions)) {
-                        // If the "stay on while plugged in" option is
-                        // turned on, then the screen will often not
-                        // automatically turn off while plugged in.  To
-                        // still have a sense of when it is inactive, we
-                        // will then count going dim as turning off.
-                        mScreenOffTime = SystemClock.elapsedRealtime();
-                    }
-                    brightness = mScreenBrightnessDim;
-                }
-            }
-            if (mWaitingForFirstLightSensor && (newState & SCREEN_ON_BIT) != 0) {
-                steps = IMMEDIATE_ANIM_STEPS;
-            }
+        // Phase 3: Send notifications, if needed.
+        sendPendingNotificationsLocked();
 
-            long identity = Binder.clearCallingIdentity();
-            try {
-                mBatteryStats.noteScreenBrightness(brightness);
-            } catch (RemoteException e) {
-                // Nothing interesting to do.
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-            if (!mSkippedScreenOn) {
-                int dt = steps * NOMINAL_FRAME_TIME_MS;
-                mScreenBrightnessAnimator.animateTo(brightness, SCREEN_BRIGHT_BIT, dt);
-                if (DEBUG_SCREEN_ON) {
-                    RuntimeException e = new RuntimeException("here");
-                    e.fillInStackTrace();
-                    Slog.i(TAG, "Setting screen brightness: " + brightness, e);
-                }
-            }
-        }
+        // Phase 4: Update suspend blocker.
+        // Because we might release the last suspend blocker here, we need to make sure
+        // we finished everything else first!
+        updateSuspendBlockerLocked();
+    }
 
-        if (DEBUG) {
-            Slog.d(TAG, "offMask=0x" + Integer.toHexString(offMask)
-                    + " dimMask=0x" + Integer.toHexString(dimMask)
-                    + " onMask=0x" + Integer.toHexString(onMask)
-                    + " difference=0x" + Integer.toHexString(difference)
-                    + " realDifference=0x" + Integer.toHexString(realDifference)
-                    + " forceState=0x" + Integer.toHexString(forceState)
-                    );
-        }
-
-        if (offMask != 0) {
-            if (DEBUG) Slog.i(TAG, "Setting brightess off: " + offMask);
-            setLightBrightness(offMask, PowerManager.BRIGHTNESS_OFF);
-        }
-        if (dimMask != 0) {
-            int brightness = mScreenBrightnessDim;
-            if ((newState & BATTERY_LOW_BIT) != 0 &&
-                    brightness > PowerManager.BRIGHTNESS_LOW_BATTERY) {
-                brightness = PowerManager.BRIGHTNESS_LOW_BATTERY;
+    private void sendPendingNotificationsLocked() {
+        if (mDisplayReady) {
+            if (mSendWakeUpFinishedNotificationWhenReady) {
+                mSendWakeUpFinishedNotificationWhenReady = false;
+                mNotifier.onWakeUpFinished();
             }
-            if (DEBUG) Slog.i(TAG, "Setting brightess dim " + brightness + ": " + dimMask);
-            setLightBrightness(dimMask, brightness);
-        }
-        if (onMask != 0) {
-            int brightness = getPreferredBrightness();
-            if ((newState & BATTERY_LOW_BIT) != 0 &&
-                    brightness > PowerManager.BRIGHTNESS_LOW_BATTERY) {
-                brightness = PowerManager.BRIGHTNESS_LOW_BATTERY;
+            if (mSendGoToSleepFinishedNotificationWhenReady) {
+                mSendGoToSleepFinishedNotificationWhenReady = false;
+                mNotifier.onGoToSleepFinished();
             }
-            if (DEBUG) Slog.i(TAG, "Setting brightess on " + brightness + ": " + onMask);
-            setLightBrightness(onMask, brightness);
         }
     }
 
     /**
-     * Note: by design this class does not hold mLocks while calling native methods.
-     * Nor should it. Ever.
+     * Updates the value of mIsPowered.
+     * Sets DIRTY_IS_POWERED if a change occurred.
      */
-    class ScreenBrightnessAnimator extends HandlerThread {
-        static final int ANIMATE_LIGHTS = 10;
-        static final int ANIMATE_POWER_OFF = 11;
-        volatile int startValue;
-        volatile int endValue;
-        volatile int startSensorValue;
-        volatile int endSensorValue;
-        volatile int currentValue;
-        private int currentMask;
-        private int duration;
-        private long startTimeMillis;
-        private final String prefix;
+    private void updateIsPoweredLocked(int dirty) {
+        if ((dirty & DIRTY_BATTERY_STATE) != 0) {
+            boolean wasPowered = mIsPowered;
+            mIsPowered = mBatteryService.isPowered();
 
-        public ScreenBrightnessAnimator(String name, int priority) {
-            super(name, priority);
-            prefix = name;
+            if (wasPowered != mIsPowered) {
+                mDirty |= DIRTY_IS_POWERED;
+
+                // Treat plugging and unplugging the devices as a user activity.
+                // Users find it disconcerting when they plug or unplug the device
+                // and it shuts off right away.
+                // Some devices also wake the device when plugged or unplugged because
+                // they don't have a charging LED.
+                final long now = SystemClock.uptimeMillis();
+                if (mWakeUpWhenPluggedOrUnpluggedConfig) {
+                    wakeUpNoUpdateLocked(now);
+                }
+                userActivityNoUpdateLocked(
+                        now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
+            }
+        }
+    }
+
+    /**
+     * Updates the value of mStayOn.
+     * Sets DIRTY_STAY_ON if a change occurred.
+     */
+    private void updateStayOnLocked(int dirty) {
+        if ((dirty & (DIRTY_BATTERY_STATE | DIRTY_SETTINGS)) != 0) {
+            if (mStayOnWhilePluggedInSetting != 0
+                    && !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
+                mStayOn = mBatteryService.isPowered(mStayOnWhilePluggedInSetting);
+            } else {
+                mStayOn = false;
+            }
+        }
+    }
+
+    /**
+     * Updates the value of mWakeLockSummary to summarize the state of all active wake locks.
+     * Note that most wake-locks are ignored when the system is asleep.
+     *
+     * This function must have no other side-effects.
+     */
+    private void updateWakeLockSummaryLocked(int dirty) {
+        if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS)) != 0) {
+            mWakeLockSummary = 0;
+
+            final int numWakeLocks = mWakeLocks.size();
+            for (int i = 0; i < numWakeLocks; i++) {
+                final WakeLock wakeLock = mWakeLocks.get(i);
+                switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+                    case PowerManager.PARTIAL_WAKE_LOCK:
+                        mWakeLockSummary |= WAKE_LOCK_CPU;
+                        break;
+                    case PowerManager.FULL_WAKE_LOCK:
+                        if (mWakefulness != WAKEFULNESS_ASLEEP) {
+                            mWakeLockSummary |= WAKE_LOCK_CPU
+                                    | WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT;
+                        }
+                        break;
+                    case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
+                        if (mWakefulness != WAKEFULNESS_ASLEEP) {
+                            mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_BRIGHT;
+                        }
+                        break;
+                    case PowerManager.SCREEN_DIM_WAKE_LOCK:
+                        if (mWakefulness != WAKEFULNESS_ASLEEP) {
+                            mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_DIM;
+                        }
+                        break;
+                    case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
+                        if (mWakefulness != WAKEFULNESS_ASLEEP) {
+                            mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_PROXIMITY_SCREEN_OFF;
+                        }
+                        break;
+                }
+            }
+
+            if (DEBUG_SPEW) {
+                Slog.d(TAG, "updateWakeLockSummaryLocked: mWakefulness="
+                        + wakefulnessToString(mWakefulness)
+                        + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary));
+            }
+        }
+    }
+
+    /**
+     * Updates the value of mUserActivitySummary to summarize the user requested
+     * state of the system such as whether the screen should be bright or dim.
+     * Note that user activity is ignored when the system is asleep.
+     *
+     * This function must have no other side-effects.
+     */
+    private void updateUserActivitySummaryLocked(long now, int dirty) {
+        // Update the status of the user activity timeout timer.
+        if ((dirty & (DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) {
+            mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);
+
+            long nextTimeout = 0;
+            if (mWakefulness != WAKEFULNESS_ASLEEP) {
+                final int screenOffTimeout = getScreenOffTimeoutLocked();
+                final int screenDimDuration = getScreenDimDurationLocked();
+
+                mUserActivitySummary = 0;
+                if (mLastUserActivityTime >= mLastWakeTime) {
+                    nextTimeout = mLastUserActivityTime
+                            + screenOffTimeout - screenDimDuration;
+                    if (now < nextTimeout) {
+                        mUserActivitySummary |= USER_ACTIVITY_SCREEN_BRIGHT;
+                    } else {
+                        nextTimeout = mLastUserActivityTime + screenOffTimeout;
+                        if (now < nextTimeout) {
+                            mUserActivitySummary |= USER_ACTIVITY_SCREEN_DIM;
+                        }
+                    }
+                }
+                if (mUserActivitySummary == 0
+                        && mLastUserActivityTimeNoChangeLights >= mLastWakeTime) {
+                    nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;
+                    if (now < nextTimeout
+                            && mDisplayPowerRequest.screenState
+                                    != DisplayPowerRequest.SCREEN_STATE_OFF) {
+                        mUserActivitySummary = mDisplayPowerRequest.screenState
+                                == DisplayPowerRequest.SCREEN_STATE_BRIGHT ?
+                                USER_ACTIVITY_SCREEN_BRIGHT : USER_ACTIVITY_SCREEN_DIM;
+                    }
+                }
+                if (mUserActivitySummary != 0) {
+                    Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);
+                    msg.setAsynchronous(true);
+                    mHandler.sendMessageAtTime(msg, nextTimeout);
+                }
+            } else {
+                mUserActivitySummary = 0;
+            }
+
+            if (DEBUG_SPEW) {
+                Slog.d(TAG, "updateUserActivitySummaryLocked: mWakefulness="
+                        + wakefulnessToString(mWakefulness)
+                        + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
+                        + ", nextTimeout=" + TimeUtils.formatUptime(nextTimeout));
+            }
+        }
+    }
+
+    /**
+     * Called when a user activity timeout has occurred.
+     * Simply indicates that something about user activity has changed so that the new
+     * state can be recomputed when the power state is updated.
+     *
+     * This function must have no other side-effects besides setting the dirty
+     * bit and calling update power state.  Wakefulness transitions are handled elsewhere.
+     */
+    private void handleUserActivityTimeout() { // runs on handler thread
+        synchronized (mLock) {
+            if (DEBUG_SPEW) {
+                Slog.d(TAG, "handleUserActivityTimeout");
+            }
+
+            mDirty |= DIRTY_USER_ACTIVITY;
+            updatePowerStateLocked();
+        }
+    }
+
+    private int getScreenOffTimeoutLocked() {
+        int timeout = mScreenOffTimeoutSetting;
+        if (isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
+            timeout = Math.min(timeout, mMaximumScreenOffTimeoutFromDeviceAdmin);
+        }
+        return Math.max(timeout, MINIMUM_SCREEN_OFF_TIMEOUT);
+    }
+
+    private int getScreenDimDurationLocked() {
+        return SCREEN_DIM_DURATION;
+    }
+
+    /**
+     * Updates the wakefulness of the device.
+     *
+     * This is the function that decides whether the device should start napping
+     * based on the current wake locks and user activity state.  It may modify mDirty
+     * if the wakefulness changes.
+     *
+     * Returns true if the wakefulness changed and we need to restart power state calculation.
+     */
+    private boolean updateWakefulnessLocked(int dirty) {
+        boolean changed = false;
+        if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED
+                | DIRTY_WAKEFULNESS | DIRTY_STAY_ON)) != 0) {
+            if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {
+                if (DEBUG_SPEW) {
+                    Slog.d(TAG, "updateWakefulnessLocked: Nap time...");
+                }
+                mWakefulness = WAKEFULNESS_NAPPING;
+                mDirty |= DIRTY_WAKEFULNESS;
+                changed = true;
+            }
+        }
+        return changed;
+    }
+
+    // Also used when exiting a dream to determine whether we should go back
+    // to being fully awake or else go to sleep for good.
+    private boolean isItBedTimeYetLocked() {
+        return mBootCompleted && !mStayOn
+                && (mWakeLockSummary
+                        & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) == 0
+                && (mUserActivitySummary
+                        & (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) == 0;
+    }
+
+    /**
+     * Determines whether to post a message to the sandman to update the dream state.
+     */
+    private void updateDreamLocked(int dirty) {
+        if ((dirty & (DIRTY_WAKEFULNESS | DIRTY_SETTINGS
+                | DIRTY_IS_POWERED | DIRTY_STAY_ON)) != 0) {
+            scheduleSandmanLocked();
+        }
+    }
+
+    private void scheduleSandmanLocked() {
+        if (!mSandmanScheduled) {
+            mSandmanScheduled = true;
+            Message msg = mHandler.obtainMessage(MSG_SANDMAN);
+            msg.setAsynchronous(true);
+            mHandler.sendMessage(msg);
+        }
+    }
+
+    /**
+     * Called when the device enters or exits a napping or dreaming state.
+     *
+     * We do this asynchronously because we must call out of the power manager to start
+     * the dream and we don't want to hold our lock while doing so.  There is a risk that
+     * the device will wake or go to sleep in the meantime so we have to handle that case.
+     */
+    private void handleSandman() { // runs on handler thread
+        // Handle preconditions.
+        boolean startDreaming = false;
+        synchronized (mLock) {
+            mSandmanScheduled = false;
+
+            if (DEBUG_SPEW) {
+                Log.d(TAG, "handleSandman: canDream=" + canDreamLocked()
+                        + ", mWakefulness=" + wakefulnessToString(mWakefulness));
+            }
+
+            if (canDreamLocked() && mWakefulness == WAKEFULNESS_NAPPING) {
+                startDreaming = true;
+            }
+        }
+
+        // Get the dream manager, if needed.
+        if (startDreaming && mDreamManager == null) {
+            mDreamManager = IDreamManager.Stub.asInterface(
+                    ServiceManager.checkService("dreams"));
+            if (mDreamManager == null) {
+                Slog.w(TAG, "Unable to find IDreamManager.");
+            }
+        }
+
+        // Start dreaming if needed.
+        // We only control the dream on the handler thread, so we don't need to worry about
+        // concurrent attempts to start or stop the dream.
+        boolean isDreaming = false;
+        if (mDreamManager != null) {
+            try {
+                isDreaming = mDreamManager.isDreaming();
+                if (startDreaming && !isDreaming) {
+                    Slog.i(TAG, "Entering dreamland.");
+                    mDreamManager.dream();
+                    isDreaming = mDreamManager.isDreaming();
+                    if (!isDreaming) {
+                        Slog.i(TAG, "Could not enter dreamland.  Sleep will be dreamless.");
+                    }
+                }
+            } catch (RemoteException ex) {
+            }
+        }
+
+        // Update dream state.
+        // We might need to stop the dream again if the preconditions changed.
+        boolean continueDreaming = false;
+        synchronized (mLock) {
+            if (isDreaming && canDreamLocked()) {
+                if (mWakefulness == WAKEFULNESS_NAPPING) {
+                    mWakefulness = WAKEFULNESS_DREAMING;
+                    mDirty |= DIRTY_WAKEFULNESS;
+                    updatePowerStateLocked();
+                    continueDreaming = true;
+                } else if (mWakefulness == WAKEFULNESS_DREAMING) {
+                    continueDreaming = true;
+                }
+            }
+            if (!continueDreaming) {
+                handleDreamFinishedLocked();
+            }
+
+            // Allow the sandman to detect when the dream has ended.
+            // FIXME: The DreamManagerService should tell us explicitly.
+            if (mWakefulness == WAKEFULNESS_DREAMING
+                    || mWakefulness == WAKEFULNESS_NAPPING) {
+                if (!mSandmanScheduled) {
+                    mSandmanScheduled = true;
+                    Message msg = mHandler.obtainMessage(MSG_SANDMAN);
+                    msg.setAsynchronous(true);
+                    mHandler.sendMessageDelayed(msg, 1000);
+                }
+            }
+        }
+
+        // Stop dreaming if needed.
+        // It's possible that something else changed to make us need to start the dream again.
+        // If so, then the power manager will have posted another message to the handler
+        // to take care of it later.
+        if (mDreamManager != null) {
+            try {
+                if (!continueDreaming && isDreaming) {
+                    Slog.i(TAG, "Leaving dreamland.");
+                    mDreamManager.awaken();
+                }
+            } catch (RemoteException ex) {
+            }
+        }
+    }
+
+    /**
+     * Returns true if the device is allowed to dream in its current state,
+     * assuming there has been no recent user activity and no wake locks are held.
+     */
+    private boolean canDreamLocked() {
+        return mIsPowered && mDreamsSupportedConfig && mDreamsEnabledSetting;
+    }
+
+    /**
+     * Called when a dream is ending to figure out what to do next.
+     */
+    private void handleDreamFinishedLocked() {
+        if (mWakefulness == WAKEFULNESS_NAPPING
+                || mWakefulness == WAKEFULNESS_DREAMING) {
+            if (isItBedTimeYetLocked()) {
+                goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
+                        PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
+                updatePowerStateLocked();
+            } else {
+                wakeUpNoUpdateLocked(SystemClock.uptimeMillis());
+                updatePowerStateLocked();
+            }
+        }
+    }
+
+
+    /**
+     * Updates the display power state asynchronously.
+     * When the update is finished, mDisplayReady will be set to true.  The display
+     * controller posts a message to tell us when the actual display power state
+     * has been updated so we come back here to double-check and finish up.
+     *
+     * This function recalculates the display power state each time.
+     */
+    private void updateDisplayPowerStateLocked(int dirty) {
+        if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
+                | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
+                | DIRTY_SETTINGS)) != 0) {
+            int newScreenState = getDesiredScreenPowerState();
+            if (newScreenState != mDisplayPowerRequest.screenState) {
+                if (newScreenState == DisplayPowerRequest.SCREEN_STATE_OFF
+                        && mDisplayPowerRequest.screenState
+                                != DisplayPowerRequest.SCREEN_STATE_OFF) {
+                    mLastScreenOffEventElapsedRealTime = SystemClock.elapsedRealtime();
+                }
+
+                mDisplayPowerRequest.screenState = newScreenState;
+                nativeSetPowerState(
+                        newScreenState != DisplayPowerRequest.SCREEN_STATE_OFF,
+                        newScreenState == DisplayPowerRequest.SCREEN_STATE_BRIGHT);
+            }
+
+            int screenBrightness = mScreenBrightnessSettingDefault;
+            boolean autoBrightness = (mScreenBrightnessModeSetting ==
+                    Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+            if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
+                screenBrightness = mScreenBrightnessOverrideFromWindowManager;
+                autoBrightness = false;
+            } else if (isValidBrightness(mTemporaryScreenBrightnessSettingOverride)) {
+                screenBrightness = mTemporaryScreenBrightnessSettingOverride;
+            } else if (isValidBrightness(mScreenBrightnessSetting)) {
+                screenBrightness =  mScreenBrightnessSetting;
+            }
+            if (autoBrightness) {
+                screenBrightness = mScreenBrightnessSettingDefault;
+            }
+            screenBrightness = Math.max(Math.min(screenBrightness,
+                    mScreenBrightnessSettingMaximum), mScreenBrightnessSettingMinimum);
+            mDisplayPowerRequest.screenBrightness = screenBrightness;
+            mDisplayPowerRequest.useAutoBrightness = autoBrightness;
+
+            mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
+
+            mDisplayReady = mDisplayPowerController.requestPowerState(mDisplayPowerRequest,
+                    mRequestWaitForNegativeProximity);
+            mRequestWaitForNegativeProximity = false;
+
+            if (DEBUG_SPEW) {
+                Slog.d(TAG, "updateScreenStateLocked: displayReady=" + mDisplayReady
+                        + ", newScreenState=" + newScreenState
+                        + ", mWakefulness=" + mWakefulness
+                        + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)
+                        + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
+                        + ", mBootCompleted=" + mBootCompleted);
+            }
+        }
+    }
+
+    private static boolean isValidBrightness(int value) {
+        return value >= 0 && value <= 255;
+    }
+
+    private int getDesiredScreenPowerState() {
+        if (mWakefulness == WAKEFULNESS_ASLEEP) {
+            return DisplayPowerRequest.SCREEN_STATE_OFF;
+        }
+
+        if ((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
+                || (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
+                || !mBootCompleted) {
+            return DisplayPowerRequest.SCREEN_STATE_BRIGHT;
+        }
+
+        return DisplayPowerRequest.SCREEN_STATE_DIM;
+    }
+
+    private final DisplayPowerController.Callbacks mDisplayPowerControllerCallbacks =
+            new DisplayPowerController.Callbacks() {
+        @Override
+        public void onStateChanged() {
+            mDirty |= DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED;
+            updatePowerStateLocked();
         }
 
         @Override
-        protected void onLooperPrepared() {
-            mScreenBrightnessHandler = new Handler() {
-                public void handleMessage(Message msg) {
-                    int brightnessMode = (mAutoBrightessEnabled && !mInitialAnimation
-                            ? LightsService.BRIGHTNESS_MODE_SENSOR
-                            : LightsService.BRIGHTNESS_MODE_USER);
-                    if (msg.what == ANIMATE_LIGHTS) {
-                        final int mask = msg.arg1;
-                        int value = msg.arg2;
-                        long tStart = SystemClock.uptimeMillis();
-                        if ((mask & SCREEN_BRIGHT_BIT) != 0) {
-                            if (DEBUG_LIGHT_ANIMATION) Slog.v(TAG, "Set brightness: " + value);
-                            mLcdLight.setBrightness(value, brightnessMode);
-                        }
-                        long elapsed = SystemClock.uptimeMillis() - tStart;
-                        if ((mask & BUTTON_BRIGHT_BIT) != 0) {
-                            mButtonLight.setBrightness(value);
-                        }
-                        if ((mask & KEYBOARD_BRIGHT_BIT) != 0) {
-                            mKeyboardLight.setBrightness(value);
-                        }
+        public void onProximityNegative() {
+            userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
+                    PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
+            updatePowerStateLocked();
+        }
+    };
 
-                        if (elapsed > 100) {
-                            Slog.e(TAG, "Excessive delay setting brightness: " + elapsed
-                                    + "ms, mask=" + mask);
-                        }
+    private boolean shouldUseProximitySensorLocked() {
+        return (mWakeLockSummary & WAKE_LOCK_PROXIMITY_SCREEN_OFF) != 0;
+    }
 
-                        // Throttle brightness updates to frame refresh rate
-                        int delay = elapsed < NOMINAL_FRAME_TIME_MS ? NOMINAL_FRAME_TIME_MS : 1;
-                        synchronized(this) {
-                            currentValue = value;
-                        }
-                        animateInternal(mask, false, delay);
-                    } else if (msg.what == ANIMATE_POWER_OFF) {
-                        int mode = msg.arg1;
-                        nativeStartSurfaceFlingerAnimation(mode);
-                    }
+    /**
+     * Updates the suspend blocker that keeps the CPU alive.
+     *
+     * This function must have no other side-effects.
+     */
+    private void updateSuspendBlockerLocked() {
+        boolean wantCpu = isCpuNeededLocked();
+        if (wantCpu != mHoldingWakeLockSuspendBlocker) {
+            mHoldingWakeLockSuspendBlocker = wantCpu;
+            if (wantCpu) {
+                if (DEBUG) {
+                    Slog.d(TAG, "updateSuspendBlockerLocked: Acquiring suspend blocker.");
                 }
-            };
-            synchronized (this) {
-                mInitComplete = true;
-                notifyAll();
-            }
-        }
-
-        private void animateInternal(int mask, boolean turningOff, int delay) {
-            synchronized (this) {
-                if (currentValue != endValue) {
-                    final long now = SystemClock.elapsedRealtime();
-                    final int elapsed = (int) (now - startTimeMillis);
-                    int newValue;
-                    if (elapsed < duration) {
-                        int delta = endValue - startValue;
-                        newValue = startValue + delta * elapsed / duration;
-                        newValue = Math.max(PowerManager.BRIGHTNESS_OFF, newValue);
-                        newValue = Math.min(PowerManager.BRIGHTNESS_ON, newValue);
-                        // Optimization to delay next step until a change will occur.
-                        if (delay > 0 && newValue == currentValue) {
-                            final int timePerStep = duration / Math.abs(delta);
-                            delay = Math.min(duration - elapsed, timePerStep);
-                            newValue += delta < 0 ? -1 : 1;
-                        }
-                        // adjust the peak sensor value until we get to the target sensor value
-                        delta = endSensorValue - startSensorValue;
-                        mHighestLightSensorValue = startSensorValue + delta * elapsed / duration;
-                    } else {
-                        newValue = endValue;
-                        mHighestLightSensorValue = endSensorValue;
-                        if (endValue > 0) {
-                            mInitialAnimation = false;
-                        }
-                    }
-
-                    if (DEBUG_LIGHT_ANIMATION) {
-                        Slog.v(TAG, "Animating light: " + "start:" + startValue
-                                + ", end:" + endValue + ", elapsed:" + elapsed
-                                + ", duration:" + duration + ", current:" + currentValue
-                                + ", newValue:" + newValue
-                                + ", delay:" + delay
-                                + ", highestSensor:" + mHighestLightSensorValue);
-                    }
-
-                    if (turningOff && !mHeadless && !mAnimateScreenLights) {
-                        int mode = mScreenOffReason == OFF_BECAUSE_OF_PROX_SENSOR
-                                ? 0 : mAnimationSetting;
-                        if (DEBUG_LIGHT_ANIMATION) {
-                            Slog.v(TAG, "Doing power-off anim, mode=" + mode);
-                        }
-                        mScreenBrightnessHandler.obtainMessage(ANIMATE_POWER_OFF, mode, 0)
-                                .sendToTarget();
-                    }
-                    mScreenBrightnessHandler.removeMessages(
-                            ScreenBrightnessAnimator.ANIMATE_LIGHTS);
-                    Message msg = mScreenBrightnessHandler
-                            .obtainMessage(ANIMATE_LIGHTS, mask, newValue);
-                    mScreenBrightnessHandler.sendMessageDelayed(msg, delay);
-                }
-            }
-        }
-
-        public void dump(PrintWriter pw, String string) {
-            pw.println(string);
-            pw.println("  animating: " + "start:" + startValue + ", end:" + endValue
-                    + ", duration:" + duration + ", current:" + currentValue);
-            pw.println("  startSensorValue:" + startSensorValue
-                    + " endSensorValue:" + endSensorValue);
-            pw.println("  startTimeMillis:" + startTimeMillis
-                    + " now:" + SystemClock.elapsedRealtime());
-            pw.println("  currentMask:" + dumpPowerState(currentMask));
-        }
-
-        public void animateTo(int target, int mask, int animationDuration) {
-            animateTo(target, mHighestLightSensorValue, mask, animationDuration);
-        }
-
-        public void animateTo(int target, int sensorTarget, int mask, int animationDuration) {
-            synchronized(this) {
-                if ((mask & SCREEN_BRIGHT_BIT) == 0) {
-                    // We only animate keyboard and button when passed in with SCREEN_BRIGHT_BIT.
-                    if ((mask & BUTTON_BRIGHT_BIT) != 0) {
-                        mButtonLight.setBrightness(target);
-                    }
-                    if ((mask & KEYBOARD_BRIGHT_BIT) != 0) {
-                        mKeyboardLight.setBrightness(target);
-                    }
-                    return;
-                }
-                if (isAnimating() && (mask ^ currentMask) != 0) {
-                    // current animation is unrelated to new animation, jump to final values
-                    cancelAnimation();
-                }
-                if (mInitialAnimation) {
-                    // jump to final value in one step the first time the brightness is set
-                    animationDuration = 0;
-                    if (target > 0) {
-                        mInitialAnimation = false;
-                    }
-                }
-                startValue = currentValue;
-                endValue = target;
-                startSensorValue = mHighestLightSensorValue;
-                endSensorValue = sensorTarget;
-                currentMask = mask;
-                duration = (int) (mWindowScaleAnimation * animationDuration);
-                startTimeMillis = SystemClock.elapsedRealtime();
-
-                if (DEBUG_LIGHT_ANIMATION) {
-                    Slog.v(TAG, "animateTo(target=" + target
-                            + ", sensor=" + sensorTarget
-                            + ", mask=" + mask
-                            + ", duration=" + animationDuration +")"
-                            + ", currentValue=" + currentValue
-                            + ", startTime=" + startTimeMillis);
-                }
-
-                if (target != currentValue) {
-                    final boolean doScreenAnim = (mask & (SCREEN_BRIGHT_BIT | SCREEN_ON_BIT)) != 0;
-                    final boolean turningOff = endValue == PowerManager.BRIGHTNESS_OFF;
-                    if (turningOff && doScreenAnim) {
-                        // Cancel all pending animations since we're turning off
-                        mScreenBrightnessHandler.removeCallbacksAndMessages(null);
-                        screenOffFinishedAnimatingLocked(mScreenOffReason);
-                        duration = 200; // TODO: how long should this be?
-                    }
-                    if (doScreenAnim) {
-                        animateInternal(mask, turningOff, 0);
-                    }
-                    // TODO: Handle keyboard light animation when we have devices that support it
-                }
-            }
-        }
-
-        public int getCurrentBrightness() {
-            synchronized (this) {
-                return currentValue;
-            }
-        }
-
-        public boolean isAnimating() {
-            synchronized (this) {
-                return currentValue != endValue;
-            }
-        }
-
-        public void cancelAnimation() {
-            animateTo(endValue, currentMask, 0);
-        }
-    }
-
-    private void setLightBrightness(int mask, int value) {
-        mScreenBrightnessAnimator.animateTo(value, mask, 0);
-    }
-
-    private int getPreferredBrightness() {
-        int brightness = mScreenBrightnessSetting;
-        if (mScreenBrightnessOverride >= 0) {
-            brightness = mScreenBrightnessOverride;
-        } else if (mLightSensorScreenBrightness >= 0 && mUseSoftwareAutoBrightness
-                && mAutoBrightessEnabled) {
-            brightness = mLightSensorScreenBrightness;
-        }
-         // Don't let applications turn the screen all the way off
-        return Math.max(brightness, mScreenBrightnessDim);
-    }
-
-    private int applyButtonState(int state) {
-        int brightness = -1;
-        if ((state & BATTERY_LOW_BIT) != 0) {
-            // do not override brightness if the battery is low
-            return state;
-        }
-        if (mButtonBrightnessOverride >= 0) {
-            brightness = mButtonBrightnessOverride;
-        } else if (mLightSensorButtonBrightness >= 0 && mUseSoftwareAutoBrightness) {
-            brightness = mLightSensorButtonBrightness;
-        }
-        if (brightness > 0) {
-            return state | BUTTON_BRIGHT_BIT;
-        } else if (brightness == 0) {
-            return state & ~BUTTON_BRIGHT_BIT;
-        } else {
-            return state;
-        }
-    }
-
-    private int applyKeyboardState(int state) {
-        int brightness = -1;
-        if ((state & BATTERY_LOW_BIT) != 0) {
-            // do not override brightness if the battery is low
-            return state;
-        }
-        if (!mKeyboardVisible) {
-            brightness = 0;
-        } else if (mButtonBrightnessOverride >= 0) {
-            brightness = mButtonBrightnessOverride;
-        } else if (mLightSensorKeyboardBrightness >= 0 && mUseSoftwareAutoBrightness) {
-            brightness =  mLightSensorKeyboardBrightness;
-        }
-        if (brightness > 0) {
-            return state | KEYBOARD_BRIGHT_BIT;
-        } else if (brightness == 0) {
-            return state & ~KEYBOARD_BRIGHT_BIT;
-        } else {
-            return state;
-        }
-    }
-
-    public boolean isScreenOn() {
-        synchronized (mLocks) {
-            return (mPowerState & SCREEN_ON_BIT) != 0;
-        }
-    }
-
-    boolean isScreenBright() {
-        synchronized (mLocks) {
-            return (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT;
-        }
-    }
-
-    private boolean isScreenTurningOffLocked() {
-        return (mScreenBrightnessAnimator.isAnimating()
-                && mScreenBrightnessAnimator.endValue == PowerManager.BRIGHTNESS_OFF
-                && (mScreenBrightnessAnimator.currentMask & SCREEN_BRIGHT_BIT) != 0);
-    }
-
-    private boolean shouldLog(long time) {
-        synchronized (mLocks) {
-            if (time > (mWarningSpewThrottleTime + (60*60*1000))) {
-                mWarningSpewThrottleTime = time;
-                mWarningSpewThrottleCount = 0;
-                return true;
-            } else if (mWarningSpewThrottleCount < 30) {
-                mWarningSpewThrottleCount++;
-                return true;
+                mWakeLockSuspendBlocker.acquire();
             } else {
-                return false;
-            }
-        }
-    }
-
-    private void forceUserActivityLocked() {
-        if (isScreenTurningOffLocked()) {
-            // cancel animation so userActivity will succeed
-            mScreenBrightnessAnimator.cancelAnimation();
-        }
-        boolean savedActivityAllowed = mUserActivityAllowed;
-        mUserActivityAllowed = true;
-        userActivity(SystemClock.uptimeMillis(), false);
-        mUserActivityAllowed = savedActivityAllowed;
-    }
-
-    public void userActivityWithForce(long time, boolean noChangeLights, boolean force) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
-        userActivity(time, -1, noChangeLights, PowerManager.USER_ACTIVITY_EVENT_OTHER, force, false);
-    }
-
-    public void userActivity(long time, boolean noChangeLights) {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
-                != PackageManager.PERMISSION_GRANTED) {
-            if (shouldLog(time)) {
-                Slog.w(TAG, "Caller does not have DEVICE_POWER permission.  pid="
-                        + Binder.getCallingPid() + " uid=" + Binder.getCallingUid());
-            }
-            return;
-        }
-
-        userActivity(time, -1, noChangeLights, PowerManager.USER_ACTIVITY_EVENT_OTHER, false, false);
-    }
-
-    public void userActivity(long time, boolean noChangeLights, int eventType) {
-        userActivity(time, -1, noChangeLights, eventType, false, false);
-    }
-
-    public void userActivity(long time, boolean noChangeLights, int eventType, boolean force) {
-        userActivity(time, -1, noChangeLights, eventType, force, false);
-    }
-
-    /*
-     * Reset the user activity timeout to now + timeout.  This overrides whatever else is going
-     * on with user activity.  Don't use this function.
-     */
-    public void clearUserActivityTimeout(long now, long timeout) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
-        Slog.i(TAG, "clearUserActivity for " + timeout + "ms from now");
-        userActivity(now, timeout, false, PowerManager.USER_ACTIVITY_EVENT_OTHER, false, false);
-    }
-
-    private void userActivity(long time, long timeoutOverride, boolean noChangeLights,
-            int eventType, boolean force, boolean ignoreIfScreenOff) {
-
-        if (((mPokey & POKE_LOCK_IGNORE_TOUCH_EVENTS) != 0) && (eventType == PowerManager.USER_ACTIVITY_EVENT_TOUCH)) {
-            if (false) {
-                Slog.d(TAG, "dropping touch mPokey=0x" + Integer.toHexString(mPokey));
-            }
-            return;
-        }
-
-        synchronized (mLocks) {
-            if (DEBUG) {
-                Slog.d(TAG, "userActivity mLastEventTime=" + mLastEventTime + " time=" + time
-                        + " mUserActivityAllowed=" + mUserActivityAllowed
-                        + " mUserState=0x" + Integer.toHexString(mUserState)
-                        + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState)
-                        + " mProximitySensorActive=" + mProximitySensorActive
-                        + " timeoutOverride=" + timeoutOverride
-                        + " force=" + force);
-            }
-            // ignore user activity if we are in the process of turning off the screen
-            if (isScreenTurningOffLocked()) {
-                Slog.d(TAG, "ignoring user activity while turning off screen");
-                return;
-            }
-            // ignore if the caller doesn't want this to allow the screen to turn
-            // on, and the screen is currently off.
-            if (ignoreIfScreenOff && (mPowerState & SCREEN_ON_BIT) == 0) {
-                return;
-            }
-            // Disable proximity sensor if if user presses power key while we are in the
-            // "waiting for proximity sensor to go negative" state.
-            if (mProximitySensorActive && mProximityWakeLockCount == 0) {
-                mProximitySensorActive = false;
-            }
-            if (mLastEventTime <= time || force) {
-                mLastEventTime = time;
-                if ((mUserActivityAllowed && !mProximitySensorActive) || force) {
-                    // Only turn on button backlights if a button was pressed
-                    // and auto brightness is disabled
-                    if (eventType == PowerManager.USER_ACTIVITY_EVENT_BUTTON && !mUseSoftwareAutoBrightness) {
-                        mUserState = (mKeyboardVisible ? ALL_BRIGHT : SCREEN_BUTTON_BRIGHT);
-                    } else {
-                        // don't clear button/keyboard backlights when the screen is touched.
-                        mUserState |= SCREEN_BRIGHT;
-                    }
-
-                    int uid = Binder.getCallingUid();
-                    long ident = Binder.clearCallingIdentity();
-                    try {
-                        mBatteryStats.noteUserActivity(uid, eventType);
-                    } catch (RemoteException e) {
-                        // Ignore
-                    } finally {
-                        Binder.restoreCallingIdentity(ident);
-                    }
-
-                    mWakeLockState = mLocks.reactivateScreenLocksLocked();
-                    setPowerState(mUserState | mWakeLockState, noChangeLights,
-                            WindowManagerPolicy.OFF_BECAUSE_OF_USER);
-                    setTimeoutLocked(time, timeoutOverride, SCREEN_BRIGHT);
+                if (DEBUG) {
+                    Slog.d(TAG, "updateSuspendBlockerLocked: Releasing suspend blocker.");
                 }
+                mWakeLockSuspendBlocker.release();
             }
         }
-
-        if (mPolicy != null) {
-            mPolicy.userActivity();
-        }
     }
 
-    private int getAutoBrightnessValue(int sensorValue, int[] values) {
+    private boolean isCpuNeededLocked() {
+        return !mBootCompleted
+                || mWakeLockSummary != 0
+                || mUserActivitySummary != 0
+                || mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF
+                || !mDisplayReady;
+    }
+
+    @Override // Binder call
+    public boolean isScreenOn() {
+        final long ident = Binder.clearCallingIdentity();
         try {
-            int i;
-            for (i = 0; i < mAutoBrightnessLevels.length; i++) {
-                if (sensorValue < mAutoBrightnessLevels[i]) {
-                    break;
-                }
-            }
-            // This is the range of brightness values that we can use.
-            final int minval = values[0];
-            final int maxval = values[mAutoBrightnessLevels.length];
-            // This is the range we will be scaling.  We put some padding
-            // at the low and high end to give the adjustment a little better
-            // impact on the actual observed value.
-            final int range = (maxval-minval) + LIGHT_SENSOR_RANGE_EXPANSION;
-            // This is the desired brightness value from 0.0 to 1.0.
-            float valf = ((values[i]-minval+(LIGHT_SENSOR_RANGE_EXPANSION/2))/(float)range);
-            // Apply a scaling to the value based on the adjustment.
-            if (mLightSensorAdjustSetting > 0 && mLightSensorAdjustSetting <= 1) {
-                float adj = (float)Math.sqrt(1.0f-mLightSensorAdjustSetting);
-                if (adj <= .00001) {
-                    valf = 1;
-                } else {
-                    valf /= adj;
-                }
-            } else if (mLightSensorAdjustSetting < 0 && mLightSensorAdjustSetting >= -1) {
-                float adj = (float)Math.sqrt(1.0f+mLightSensorAdjustSetting);
-                valf *= adj;
-            }
-            // Apply an additional offset to the value based on the adjustment.
-            valf += mLightSensorAdjustSetting/LIGHT_SENSOR_OFFSET_SCALE;
-            // Convert the 0.0-1.0 value back to a brightness integer.
-            int val = (int)((valf*range)+minval) - (LIGHT_SENSOR_RANGE_EXPANSION/2);
-            if (val < minval) val = minval;
-            else if (val > maxval) val = maxval;
-            return val;
-        } catch (Exception e) {
-            // guard against null pointer or index out of bounds errors
-            Slog.e(TAG, "Values array must be non-empty and must be one element longer than "
-                    + "the auto-brightness levels array.  Check config.xml.", e);
-            return 255;
+            return isScreenOnInternal();
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
-    private Runnable mProximityTask = new Runnable() {
-        public void run() {
-            synchronized (mLocks) {
-                if (mProximityPendingValue != -1) {
-                    proximityChangedLocked(mProximityPendingValue == 1);
-                    mProximityPendingValue = -1;
-                }
-                if (mProximityPartialLock.isHeld()) {
-                    mProximityPartialLock.release();
-                }
-            }
-        }
-    };
-
-    private Runnable mAutoBrightnessTask = new Runnable() {
-        public void run() {
-            synchronized (mLocks) {
-                if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) {
-                    int value = (int)mLightSensorPendingValue;
-                    mLightSensorPendingDecrease = false;
-                    mLightSensorPendingIncrease = false;
-                    lightSensorChangedLocked(value, false);
-                }
-            }
-        }
-    };
-
-    /** used to prevent lightsensor changes while turning on. */
-    private boolean mInitialAnimation = true;
-
-    private void dockStateChanged(int state) {
-        synchronized (mLocks) {
-            mIsDocked = (state != Intent.EXTRA_DOCK_STATE_UNDOCKED);
-            if (mIsDocked) {
-                // allow brightness to decrease when docked
-                mHighestLightSensorValue = -1;
-            }
-            if ((mPowerState & SCREEN_ON_BIT) != 0) {
-                // force lights recalculation
-                int value = (int)mLightSensorValue;
-                mLightSensorValue = -1;
-                lightSensorChangedLocked(value, false);
-            }
+    private boolean isScreenOnInternal() {
+        synchronized (mLock) {
+            return !mSystemReady
+                    || mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF;
         }
     }
 
-    private void lightSensorChangedLocked(int value, boolean immediate) {
-        if (DEBUG_LIGHT_SENSOR) {
-            Slog.d(TAG, "lightSensorChangedLocked value=" + value + " immediate=" + immediate);
-        }
-
-        // Don't do anything if the screen is off.
-        if ((mPowerState & SCREEN_ON_BIT) == 0) {
-            if (DEBUG_LIGHT_SENSOR) {
-                Slog.d(TAG, "dropping lightSensorChangedLocked because screen is off");
-            }
-            return;
-        }
-
-        if (mLightSensorValue != value) {
-            mLightSensorValue = value;
-            if ((mPowerState & BATTERY_LOW_BIT) == 0) {
-                // use maximum light sensor value seen since screen went on for LCD to avoid flicker
-                // we only do this if we are undocked, since lighting should be stable when
-                // stationary in a dock.
-                int lcdValue = getAutoBrightnessValue(value, mLcdBacklightValues);
-                int buttonValue = getAutoBrightnessValue(value, mButtonBacklightValues);
-                int keyboardValue;
-                if (mKeyboardVisible) {
-                    keyboardValue = getAutoBrightnessValue(value, mKeyboardBacklightValues);
-                } else {
-                    keyboardValue = 0;
-                }
-                mLightSensorScreenBrightness = lcdValue;
-                mLightSensorButtonBrightness = buttonValue;
-                mLightSensorKeyboardBrightness = keyboardValue;
-
-                if (DEBUG_LIGHT_SENSOR) {
-                    Slog.d(TAG, "lcdValue " + lcdValue);
-                    Slog.d(TAG, "buttonValue " + buttonValue);
-                    Slog.d(TAG, "keyboardValue " + keyboardValue);
-                }
-
-                if (mAutoBrightessEnabled && mScreenBrightnessOverride < 0) {
-                    if (!mSkippedScreenOn && !mInitialAnimation) {
-                        final int steps;
-                        if (immediate) {
-                            steps = IMMEDIATE_ANIM_STEPS;
-                        } else {
-                            synchronized (mScreenBrightnessAnimator) {
-                                if (mScreenBrightnessAnimator.currentValue <= lcdValue) {
-                                    steps = AUTOBRIGHTNESS_ANIM_STEPS;
-                                } else {
-                                    steps = AUTODIMNESS_ANIM_STEPS;
-                                }
-                            }
-                        }
-                        mScreenBrightnessAnimator.animateTo(lcdValue, value,
-                                SCREEN_BRIGHT_BIT, steps * NOMINAL_FRAME_TIME_MS);
-                    }
-                }
-                if (mButtonBrightnessOverride < 0) {
-                    mButtonLight.setBrightness(buttonValue);
-                }
-                if (mButtonBrightnessOverride < 0 || !mKeyboardVisible) {
-                    mKeyboardLight.setBrightness(keyboardValue);
-                }
-            }
-        }
+    private void handleBatteryStateChangedLocked() {
+        mDirty |= DIRTY_BATTERY_STATE;
+        updatePowerStateLocked();
     }
 
-    /**
-     * The user requested that we go to sleep (probably with the power button).
-     * This overrides all wake locks that are held.
-     */
-    public void goToSleep(long time)
-    {
-        goToSleepWithReason(time, WindowManagerPolicy.OFF_BECAUSE_OF_USER);
+    private void handleBootCompletedLocked() {
+        final long now = SystemClock.uptimeMillis();
+        mBootCompleted = true;
+        mDirty |= DIRTY_BOOT_COMPLETED;
+        userActivityNoUpdateLocked(
+                now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
+        updatePowerStateLocked();
     }
 
-    /**
-     * The user requested that we go to sleep (probably with the power button).
-     * This overrides all wake locks that are held.
-     */
-    public void goToSleepWithReason(long time, int reason)
-    {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
-        synchronized (mLocks) {
-            goToSleepLocked(time, reason);
-        }
+    private void handleDockStateChangedLocked(int dockState) {
+        // TODO
     }
 
     /**
      * Reboot the device immediately, passing 'reason' (may be null)
      * to the underlying __reboot system call.  Should not return.
      */
-    public void reboot(String reason)
-    {
+    @Override // Binder call
+    public void reboot(String reason) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
 
-        if (mHandler == null || !ActivityManagerNative.isSystemReady()) {
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            rebootInternal(reason);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private void rebootInternal(final String reason) {
+        if (mHandler == null || !mSystemReady) {
             throw new IllegalStateException("Too early to call reboot()");
         }
 
-        final String finalReason = reason;
         Runnable runnable = new Runnable() {
             public void run() {
                 synchronized (this) {
-                    ShutdownThread.reboot(mContext, finalReason, false);
+                    ShutdownThread.reboot(mContext, reason, false);
                 }
-
             }
         };
+
         // ShutdownThread must run on a looper capable of displaying the UI.
-        mHandler.post(runnable);
+        Message msg = Message.obtain(mHandler, runnable);
+        msg.setAsynchronous(true);
+        mHandler.sendMessage(msg);
 
         // PowerManager.reboot() is documented not to return so just wait for the inevitable.
         synchronized (runnable) {
@@ -2798,11 +1463,23 @@
      * Crash the runtime (causing a complete restart of the Android framework).
      * Requires REBOOT permission.  Mostly for testing.  Should not return.
      */
-    public void crash(final String message)
-    {
+    @Override // Binder call
+    public void crash(String message) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            crashInternal(message);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private void crashInternal(final String message) {
         Thread t = new Thread("PowerManagerService.crash()") {
-            public void run() { throw new RuntimeException(message); }
+            public void run() {
+                throw new RuntimeException(message);
+            }
         };
         try {
             t.start();
@@ -2812,581 +1489,554 @@
         }
     }
 
-    private void goToSleepLocked(long time, int reason) {
-        if (DEBUG) {
-            Exception ex = new Exception();
-            ex.fillInStackTrace();
-            Slog.d(TAG, "goToSleep mLastEventTime=" + mLastEventTime + " time=" + time
-                    + " reason=" + reason, ex);
-        }
-
-        if (mLastEventTime <= time) {
-            mLastEventTime = time;
-            // cancel all of the wake locks
-            mWakeLockState = SCREEN_OFF;
-            int N = mLocks.size();
-            int numCleared = 0;
-            boolean proxLock = false;
-            for (int i=0; i<N; i++) {
-                WakeLock wl = mLocks.get(i);
-                if (isScreenLock(wl.flags)) {
-                    if (((wl.flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)
-                            && reason == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) {
-                        proxLock = true;
-                    } else {
-                        mLocks.get(i).activated = false;
-                        numCleared++;
-                    }
-                }
-            }
-            if (!proxLock) {
-                mProxIgnoredBecauseScreenTurnedOff = true;
-                if (DEBUG_PROXIMITY_SENSOR) {
-                    Slog.d(TAG, "setting mProxIgnoredBecauseScreenTurnedOff");
-                }
-            }
-            EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numCleared);
-            mStillNeedSleepNotification = true;
-            mUserState = SCREEN_OFF;
-            setPowerState(SCREEN_OFF, false, reason);
-            cancelTimerLocked();
-        }
+    @Override // Binder call
+    public void clearUserActivityTimeout(long now, long timeout) {
+        // TODO Auto-generated method stub
+        // Only used by phone app, delete this
     }
 
-    public long timeSinceScreenOn() {
-        synchronized (mLocks) {
-            if ((mPowerState & SCREEN_ON_BIT) != 0) {
-                return 0;
-            }
-            return SystemClock.elapsedRealtime() - mScreenOffTime;
-        }
-    }
-
-    public void setKeyboardVisibility(boolean visible) {
-        synchronized (mLocks) {
-            if (DEBUG) {
-                Slog.d(TAG, "setKeyboardVisibility: " + visible);
-            }
-            if (mKeyboardVisible != visible) {
-                mKeyboardVisible = visible;
-                // don't signal user activity if the screen is off; other code
-                // will take care of turning on due to a true change to the lid
-                // switch and synchronized with the lock screen.
-                if ((mPowerState & SCREEN_ON_BIT) != 0) {
-                    if (mUseSoftwareAutoBrightness) {
-                        // force recompute of backlight values
-                        if (mLightSensorValue >= 0) {
-                            int value = (int)mLightSensorValue;
-                            mLightSensorValue = -1;
-                            lightSensorChangedLocked(value, false);
-                        }
-                    }
-                    userActivity(SystemClock.uptimeMillis(), false, PowerManager.USER_ACTIVITY_EVENT_BUTTON, true);
-                }
-            }
-        }
+    @Override // Binder call
+    public void setPokeLock(int pokey, IBinder lock, String tag) {
+        // TODO Auto-generated method stub
+        // Only used by phone app, delete this
     }
 
     /**
-     * When the keyguard is up, it manages the power state, and userActivity doesn't do anything.
-     * When disabling user activity we also reset user power state so the keyguard can reset its
-     * short screen timeout when keyguard is unhidden.
+     * Set the setting that determines whether the device stays on when plugged in.
+     * The argument is a bit string, with each bit specifying a power source that,
+     * when the device is connected to that source, causes the device to stay on.
+     * See {@link android.os.BatteryManager} for the list of power sources that
+     * can be specified. Current values include {@link android.os.BatteryManager#BATTERY_PLUGGED_AC}
+     * and {@link android.os.BatteryManager#BATTERY_PLUGGED_USB}
+     *
+     * Used by "adb shell svc power stayon ..."
+     *
+     * @param val an {@code int} containing the bits that specify which power sources
+     * should cause the device to stay on.
      */
-    public void enableUserActivity(boolean enabled) {
-        if (DEBUG) {
-            Slog.d(TAG, "enableUserActivity " + enabled);
-        }
-        synchronized (mLocks) {
-            mUserActivityAllowed = enabled;
-            if (!enabled) {
-                // cancel timeout and clear mUserState so the keyguard can set a short timeout
-                setTimeoutLocked(SystemClock.uptimeMillis(), 0);
-            }
+    @Override // Binder call
+    public void setStayOnSetting(int val) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SETTINGS, null);
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            setStayOnSettingInternal(val);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
-    private void setScreenBrightnessMode(int mode) {
-        synchronized (mLocks) {
-            boolean enabled = (mode == SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-            if (mUseSoftwareAutoBrightness && mAutoBrightessEnabled != enabled) {
-                mAutoBrightessEnabled = enabled;
-                // This will get us a new value
-                enableLightSensorLocked(mAutoBrightessEnabled && isScreenOn());
-            }
-        }
-    }
-
-    /** Sets the screen off timeouts:
-     *      mKeylightDelay
-     *      mDimDelay
-     *      mScreenOffDelay
-     * */
-    private void setScreenOffTimeoutsLocked() {
-        if ((mPokey & POKE_LOCK_SHORT_TIMEOUT) != 0) {
-            mKeylightDelay = mShortKeylightDelay;  // Configurable via secure settings
-            mDimDelay = -1;
-            mScreenOffDelay = 0;
-        } else if ((mPokey & POKE_LOCK_MEDIUM_TIMEOUT) != 0) {
-            mKeylightDelay = MEDIUM_KEYLIGHT_DELAY;
-            mDimDelay = -1;
-            mScreenOffDelay = 0;
-        } else {
-            int totalDelay = mScreenOffTimeoutSetting;
-            if (totalDelay > mMaximumScreenOffTimeout) {
-                totalDelay = mMaximumScreenOffTimeout;
-            }
-            mKeylightDelay = LONG_KEYLIGHT_DELAY;
-            if (totalDelay < 0) {
-                // negative number means stay on as long as possible.
-                mScreenOffDelay = mMaximumScreenOffTimeout;
-            } else if (mKeylightDelay < totalDelay) {
-                // subtract the time that the keylight delay. This will give us the
-                // remainder of the time that we need to sleep to get the accurate
-                // screen off timeout.
-                mScreenOffDelay = totalDelay - mKeylightDelay;
-            } else {
-                mScreenOffDelay = 0;
-            }
-            if (mDimScreen && totalDelay >= (LONG_KEYLIGHT_DELAY + LONG_DIM_TIME)) {
-                mDimDelay = mScreenOffDelay - LONG_DIM_TIME;
-                mScreenOffDelay = LONG_DIM_TIME;
-            } else {
-                mDimDelay = -1;
-            }
-        }
-        if (DEBUG) {
-            Slog.d(TAG, "setScreenOffTimeouts mKeylightDelay=" + mKeylightDelay
-                    + " mDimDelay=" + mDimDelay + " mScreenOffDelay=" + mScreenOffDelay
-                    + " mDimScreen=" + mDimScreen);
-        }
+    private void setStayOnSettingInternal(int val) {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.STAY_ON_WHILE_PLUGGED_IN, val);
     }
 
     /**
-     * Refreshes cached secure settings.  Called once on startup, and
-     * on subsequent changes to secure settings.
+     * Used by device administration to set the maximum screen off timeout.
+     *
+     * This method must only be called by the device administration policy manager.
      */
-    private void updateSettingsValues() {
-        mShortKeylightDelay = Settings.Secure.getInt(
-                mContext.getContentResolver(),
-                Settings.Secure.SHORT_KEYLIGHT_DELAY_MS,
-                SHORT_KEYLIGHT_DELAY_DEFAULT);
-        // Slog.i(TAG, "updateSettingsValues(): mShortKeylightDelay now " + mShortKeylightDelay);
-    }
-
-    private class LockList extends ArrayList<WakeLock>
-    {
-        void addLock(WakeLock wl)
-        {
-            int index = getIndex(wl.binder);
-            if (index < 0) {
-                this.add(wl);
-            }
-        }
-
-        WakeLock removeLock(IBinder binder)
-        {
-            int index = getIndex(binder);
-            if (index >= 0) {
-                return this.remove(index);
-            } else {
-                return null;
-            }
-        }
-
-        int getIndex(IBinder binder)
-        {
-            int N = this.size();
-            for (int i=0; i<N; i++) {
-                if (this.get(i).binder == binder) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        int gatherState()
-        {
-            int result = 0;
-            int N = this.size();
-            for (int i=0; i<N; i++) {
-                WakeLock wl = this.get(i);
-                if (wl.activated) {
-                    if (isScreenLock(wl.flags)) {
-                        result |= wl.minState;
-                    }
-                }
-            }
-            return result;
-        }
-
-        int reactivateScreenLocksLocked()
-        {
-            int result = 0;
-            int N = this.size();
-            for (int i=0; i<N; i++) {
-                WakeLock wl = this.get(i);
-                if (isScreenLock(wl.flags)) {
-                    wl.activated = true;
-                    result |= wl.minState;
-                }
-            }
-            if (DEBUG_PROXIMITY_SENSOR) {
-                Slog.d(TAG, "reactivateScreenLocksLocked mProxIgnoredBecauseScreenTurnedOff="
-                        + mProxIgnoredBecauseScreenTurnedOff);
-            }
-            mProxIgnoredBecauseScreenTurnedOff = false;
-            return result;
+    @Override // Binder call
+    public void setMaximumScreenOffTimeoutFromDeviceAdmin(int timeMs) {
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            setMaximumScreenOffTimeoutFromDeviceAdminInternal(timeMs);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
-    public void setPolicy(WindowManagerPolicy p) {
-        synchronized (mLocks) {
-            mPolicy = p;
-            mLocks.notifyAll();
+    private void setMaximumScreenOffTimeoutFromDeviceAdminInternal(int timeMs) {
+        synchronized (mLock) {
+            mMaximumScreenOffTimeoutFromDeviceAdmin = timeMs;
+            mDirty |= DIRTY_SETTINGS;
+            updatePowerStateLocked();
         }
     }
 
-    WindowManagerPolicy getPolicyLocked() {
-        while (mPolicy == null || !mDoneBooting) {
-            try {
-                mLocks.wait();
-            } catch (InterruptedException e) {
-                // Ignore
-            }
-        }
-        return mPolicy;
+    private boolean isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() {
+        return mMaximumScreenOffTimeoutFromDeviceAdmin >= 0
+                && mMaximumScreenOffTimeoutFromDeviceAdmin < Integer.MAX_VALUE;
     }
 
-    public void systemReady() {
-        mSensorManager = new SystemSensorManager(mHandlerThread.getLooper());
-        mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
-        // don't bother with the light sensor if auto brightness is handled in hardware
-        if (mUseSoftwareAutoBrightness) {
-            mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
-        }
-
-        // wait until sensors are enabled before turning on screen.
-        // some devices will not activate the light sensor properly on boot
-        // unless we do this.
-        if (mUseSoftwareAutoBrightness) {
-            // turn the screen on
-            setPowerState(SCREEN_BRIGHT);
-        } else {
-            // turn everything on
-            setPowerState(ALL_BRIGHT);
-        }
-
-        synchronized (mLocks) {
-            Slog.d(TAG, "system ready!");
-            mDoneBooting = true;
-
-            enableLightSensorLocked(mUseSoftwareAutoBrightness && mAutoBrightessEnabled);
-
-            long identity = Binder.clearCallingIdentity();
-            try {
-                mBatteryStats.noteScreenBrightness(getPreferredBrightness());
-                mBatteryStats.noteScreenOn();
-            } catch (RemoteException e) {
-                // Nothing interesting to do.
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
+    @Override // Binder call
+    public void preventScreenOn(boolean prevent) {
+        // TODO Auto-generated method stub
+        // Only used by phone app, delete this
     }
 
-    void bootCompleted() {
-        Slog.d(TAG, "bootCompleted");
-        synchronized (mLocks) {
-            mBootCompleted = true;
-            userActivity(SystemClock.uptimeMillis(), false, PowerManager.USER_ACTIVITY_EVENT_BUTTON, true);
-            updateWakeLockLocked();
-            mLocks.notifyAll();
-        }
-    }
-
-    // for watchdog
-    public void monitor() {
-        synchronized (mLocks) { }
-    }
-
-    public int getSupportedWakeLockFlags() {
-        int result = PowerManager.PARTIAL_WAKE_LOCK
-                   | PowerManager.FULL_WAKE_LOCK
-                   | PowerManager.SCREEN_DIM_WAKE_LOCK;
-
-        if (mProximitySensor != null) {
-            result |= PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK;
-        }
-
-        return result;
-    }
-
-    public void setBacklightBrightness(int brightness) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
-        // Don't let applications turn the screen all the way off
-        synchronized (mLocks) {
-            brightness = Math.max(brightness, mScreenBrightnessDim);
-            mLcdLight.setBrightness(brightness);
-            mKeyboardLight.setBrightness(mKeyboardVisible ? brightness : 0);
-            mButtonLight.setBrightness(brightness);
-            long identity = Binder.clearCallingIdentity();
-            try {
-                mBatteryStats.noteScreenBrightness(brightness);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "RemoteException calling noteScreenBrightness on BatteryStatsService", e);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-            mScreenBrightnessAnimator.animateTo(brightness, SCREEN_BRIGHT_BIT, 0);
-        }
-    }
-
-    public void setAutoBrightnessAdjustment(float adj) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
-        synchronized (mLocks) {
-            mLightSensorAdjustSetting = adj;
-            if (mSensorManager != null && mLightSensorEnabled) {
-                // clear calling identity so sensor manager battery stats are accurate
-                long identity = Binder.clearCallingIdentity();
-                try {
-                    // force recompute of backlight values
-                    if (mLightSensorValue >= 0) {
-                        int value = (int)mLightSensorValue;
-                        mLightSensorValue = -1;
-                        handleLightSensorValue(value, true);
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(identity);
-                }
-            }
-        }
-    }
-
+    /**
+     * Used by the phone application to make the attention LED flash when ringing.
+     */
+    @Override // Binder call
     public void setAttentionLight(boolean on, int color) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
-        mAttentionLight.setFlashing(color, LightsService.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0);
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            setAttentionLightInternal(on, color);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
     }
 
-    private void enableProximityLockLocked() {
-        if (DEBUG_PROXIMITY_SENSOR) {
-            Slog.d(TAG, "enableProximityLockLocked");
+    private void setAttentionLightInternal(boolean on, int color) {
+        LightsService.Light light;
+        synchronized (mLock) {
+            if (!mSystemReady) {
+                return;
+            }
+            light = mAttentionLight;
         }
-        if (!mProximitySensorEnabled) {
-            // clear calling identity so sensor manager battery stats are accurate
-            long identity = Binder.clearCallingIdentity();
-            try {
-                mSensorManager.registerListener(mProximityListener, mProximitySensor,
-                        SensorManager.SENSOR_DELAY_NORMAL);
-                mProximitySensorEnabled = true;
-            } finally {
-                Binder.restoreCallingIdentity(identity);
+
+        // Control light outside of lock.
+        light.setFlashing(color, LightsService.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0);
+    }
+
+    /**
+     * Used by the Watchdog.
+     */
+    public long timeSinceScreenWasLastOn() {
+        synchronized (mLock) {
+            if (mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF) {
+                return 0;
+            }
+            return SystemClock.elapsedRealtime() - mLastScreenOffEventElapsedRealTime;
+        }
+    }
+
+    /**
+     * Used by the window manager to override the screen brightness based on the
+     * current foreground activity.
+     *
+     * This method must only be called by the window manager.
+     *
+     * @param brightness The overridden brightness, or -1 to disable the override.
+     */
+    public void setScreenBrightnessOverrideFromWindowManager(int brightness) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            setScreenBrightnessOverrideFromWindowManagerInternal(brightness);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private void setScreenBrightnessOverrideFromWindowManagerInternal(int brightness) {
+        synchronized (mLock) {
+            if (mScreenBrightnessOverrideFromWindowManager != brightness) {
+                mScreenBrightnessOverrideFromWindowManager = brightness;
+                mDirty |= DIRTY_SETTINGS;
+                updatePowerStateLocked();
             }
         }
     }
 
-    private void disableProximityLockLocked() {
-        if (DEBUG_PROXIMITY_SENSOR) {
-            Slog.d(TAG, "disableProximityLockLocked");
+    /**
+     * Used by the window manager to override the button brightness based on the
+     * current foreground activity.
+     *
+     * This method must only be called by the window manager.
+     *
+     * @param brightness The overridden brightness, or -1 to disable the override.
+     */
+    public void setButtonBrightnessOverrideFromWindowManager(int brightness) {
+        // Do nothing.
+        // Button lights are not currently supported in the new implementation.
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+    }
+
+    /**
+     * Used by the settings application and brightness control widgets to
+     * temporarily override the current screen brightness setting so that the
+     * user can observe the effect of an intended settings change without applying
+     * it immediately.
+     *
+     * The override will be canceled when the setting value is next updated.
+     *
+     * @param brightness The overridden brightness.
+     *
+     * @see Settings.System#SCREEN_BRIGHTNESS
+     */
+    @Override // Binder call
+    public void setTemporaryScreenBrightnessSettingOverride(int brightness) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            setTemporaryScreenBrightnessSettingOverrideInternal(brightness);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
-        if (mProximitySensorEnabled) {
-            // clear calling identity so sensor manager battery stats are accurate
-            long identity = Binder.clearCallingIdentity();
-            try {
-                mSensorManager.unregisterListener(mProximityListener);
-                mHandler.removeCallbacks(mProximityTask);
-                if (mProximityPartialLock.isHeld()) {
-                    mProximityPartialLock.release();
-                }
-                mProximitySensorEnabled = false;
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-            if (mProximitySensorActive) {
-                mProximitySensorActive = false;
-                if (DEBUG_PROXIMITY_SENSOR) {
-                    Slog.d(TAG, "disableProximityLockLocked mProxIgnoredBecauseScreenTurnedOff="
-                            + mProxIgnoredBecauseScreenTurnedOff);
-                }
-                if (!mProxIgnoredBecauseScreenTurnedOff) {
-                    forceUserActivityLocked();
-                }
+    }
+
+    private void setTemporaryScreenBrightnessSettingOverrideInternal(int brightness) {
+        synchronized (mLock) {
+            if (mTemporaryScreenBrightnessSettingOverride != brightness) {
+                mTemporaryScreenBrightnessSettingOverride = brightness;
+                mDirty |= DIRTY_SETTINGS;
+                updatePowerStateLocked();
             }
         }
     }
 
-    private void proximityChangedLocked(boolean active) {
-        if (DEBUG_PROXIMITY_SENSOR) {
-            Slog.d(TAG, "proximityChangedLocked, active: " + active);
+    /**
+     * Used by the settings application and brightness control widgets to
+     * temporarily override the current screen auto-brightness adjustment setting so that the
+     * user can observe the effect of an intended settings change without applying
+     * it immediately.
+     *
+     * The override will be canceled when the setting value is next updated.
+     *
+     * @param adj The overridden brightness, or -1 to disable the override.
+     *
+     * @see Settings.System#SCREEN_AUTO_BRIGHTNESS_ADJ
+     */
+    @Override // Binder call
+    public void setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(float adj) {
+        // Not implemented.
+        // The SCREEN_AUTO_BRIGHTNESS_ADJ setting is not currently supported.
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+    }
+
+    /**
+     * Low-level function turn the device off immediately, without trying
+     * to be clean.  Most people should use {@link ShutdownThread} for a clean shutdown.
+     */
+    public static void lowLevelShutdown() {
+        nativeShutdown();
+    }
+
+    /**
+     * Low-level function to reboot the device.
+     *
+     * @param reason code to pass to the kernel (e.g. "recovery"), or null.
+     * @throws IOException if reboot fails for some reason (eg, lack of
+     *         permission)
+     */
+    public static void lowLevelReboot(String reason) throws IOException {
+        nativeReboot(reason);
+    }
+
+    @Override // Watchdog.Monitor implementation
+    public void monitor() {
+        // Grab and release lock for watchdog monitor to detect deadlocks.
+        synchronized (mLock) {
         }
-        if (!mProximitySensorEnabled) {
-            Slog.d(TAG, "Ignoring proximity change after sensor is disabled");
+    }
+
+    @Override // Binder call
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            pw.println("Permission Denial: can't dump PowerManager from from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid());
             return;
         }
-        if (active) {
-            if (DEBUG_PROXIMITY_SENSOR) {
-                Slog.d(TAG, "b mProxIgnoredBecauseScreenTurnedOff="
-                        + mProxIgnoredBecauseScreenTurnedOff);
-            }
-            if (!mProxIgnoredBecauseScreenTurnedOff) {
-                goToSleepLocked(SystemClock.uptimeMillis(),
-                        WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR);
-            }
-            mProximitySensorActive = true;
-        } else {
-            // proximity sensor negative events trigger as user activity.
-            // temporarily set mUserActivityAllowed to true so this will work
-            // even when the keyguard is on.
-            mProximitySensorActive = false;
-            if (DEBUG_PROXIMITY_SENSOR) {
-                Slog.d(TAG, "b mProxIgnoredBecauseScreenTurnedOff="
-                        + mProxIgnoredBecauseScreenTurnedOff);
-            }
-            if (!mProxIgnoredBecauseScreenTurnedOff) {
-                forceUserActivityLocked();
+
+        pw.println("POWER MANAGER (dumpsys power)\n");
+
+        final DisplayPowerController dpc;
+        synchronized (mLock) {
+            pw.println("Power Manager State:");
+            pw.println("  mDirty=0x" + Integer.toHexString(mDirty));
+            pw.println("  mWakefulness=" + wakefulnessToString(mWakefulness));
+            pw.println("  mIsPowered=" + mIsPowered);
+            pw.println("  mStayOn=" + mStayOn);
+            pw.println("  mBootCompleted=" + mBootCompleted);
+            pw.println("  mSystemReady=" + mSystemReady);
+            pw.println("  mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary));
+            pw.println("  mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary));
+            pw.println("  mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity);
+            pw.println("  mSandmanScheduled=" + mSandmanScheduled);
+            pw.println("  mLastWakeTime=" + TimeUtils.formatUptime(mLastWakeTime));
+            pw.println("  mLastSleepTime=" + TimeUtils.formatUptime(mLastSleepTime));
+            pw.println("  mSendWakeUpFinishedNotificationWhenReady="
+                    + mSendWakeUpFinishedNotificationWhenReady);
+            pw.println("  mSendGoToSleepFinishedNotificationWhenReady="
+                    + mSendGoToSleepFinishedNotificationWhenReady);
+            pw.println("  mLastUserActivityTime=" + TimeUtils.formatUptime(mLastUserActivityTime));
+            pw.println("  mLastUserActivityTimeNoChangeLights="
+                    + TimeUtils.formatUptime(mLastUserActivityTimeNoChangeLights));
+            pw.println("  mDisplayReady=" + mDisplayReady);
+            pw.println("  mHoldingWakeLockSuspendBlocker=" + mHoldingWakeLockSuspendBlocker);
+
+            pw.println();
+            pw.println("Settings and Configuration:");
+            pw.println("  mDreamsSupportedConfig=" + mDreamsSupportedConfig);
+            pw.println("  mDreamsEnabledSetting=" + mDreamsEnabledSetting);
+            pw.println("  mScreenOffTimeoutSetting=" + mScreenOffTimeoutSetting);
+            pw.println("  mMaximumScreenOffTimeoutFromDeviceAdmin="
+                    + mMaximumScreenOffTimeoutFromDeviceAdmin + " (enforced="
+                    + isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() + ")");
+            pw.println("  mStayOnWhilePluggedInSetting=" + mStayOnWhilePluggedInSetting);
+            pw.println("  mScreenBrightnessSetting=" + mScreenBrightnessSetting);
+            pw.println("  mScreenBrightnessModeSetting=" + mScreenBrightnessModeSetting);
+            pw.println("  mScreenBrightnessOverrideFromWindowManager="
+                    + mScreenBrightnessOverrideFromWindowManager);
+            pw.println("  mTemporaryScreenBrightnessSettingOverride="
+                    + mTemporaryScreenBrightnessSettingOverride);
+            pw.println("  mScreenBrightnessSettingMinimum=" + mScreenBrightnessSettingMinimum);
+            pw.println("  mScreenBrightnessSettingMaximum=" + mScreenBrightnessSettingMaximum);
+            pw.println("  mScreenBrightnessSettingDefault=" + mScreenBrightnessSettingDefault);
+
+            pw.println();
+            pw.println("Wake Locks: size=" + mWakeLocks.size());
+            for (WakeLock wl : mWakeLocks) {
+                pw.println("  " + wl);
             }
 
-            if (mProximityWakeLockCount == 0) {
-                // disable sensor if we have no listeners left after proximity negative
-                disableProximityLockLocked();
+            pw.println();
+            pw.println("Suspend Blockers: size=" + mSuspendBlockers.size());
+            for (SuspendBlocker sb : mSuspendBlockers) {
+                pw.println("  " + sb);
+            }
+
+            dpc = mDisplayPowerController;
+        }
+
+        if (dpc != null) {
+            dpc.dump(pw);
+        }
+    }
+
+    private SuspendBlocker createSuspendBlockerLocked(String name) {
+        SuspendBlocker suspendBlocker = new SuspendBlockerImpl(name);
+        mSuspendBlockers.add(suspendBlocker);
+        return suspendBlocker;
+    }
+
+    private static String wakefulnessToString(int wakefulness) {
+        switch (wakefulness) {
+            case WAKEFULNESS_ASLEEP:
+                return "Asleep";
+            case WAKEFULNESS_AWAKE:
+                return "Awake";
+            case WAKEFULNESS_DREAMING:
+                return "Dreaming";
+            case WAKEFULNESS_NAPPING:
+                return "Napping";
+            default:
+                return Integer.toString(wakefulness);
+        }
+    }
+
+    private static WorkSource copyWorkSource(WorkSource workSource) {
+        return workSource != null ? new WorkSource(workSource) : null;
+    }
+
+    private final class BatteryReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (mLock) {
+                handleBatteryStateChangedLocked();
             }
         }
     }
 
-    private void enableLightSensorLocked(boolean enable) {
-        if (DEBUG_LIGHT_SENSOR) {
-            Slog.d(TAG, "enableLightSensorLocked enable=" + enable
-                    + " mLightSensorEnabled=" + mLightSensorEnabled
-                    + " mAutoBrightessEnabled=" + mAutoBrightessEnabled
-                    + " mWaitingForFirstLightSensor=" + mWaitingForFirstLightSensor);
+    private final class BootCompletedReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (mLock) {
+                handleBootCompletedLocked();
+            }
         }
-        if (!mAutoBrightessEnabled) {
-            enable = false;
+    }
+
+    private final class DockReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (mLock) {
+                int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+                        Intent.EXTRA_DOCK_STATE_UNDOCKED);
+                handleDockStateChangedLocked(dockState);
+            }
         }
-        if (mSensorManager != null && mLightSensorEnabled != enable) {
-            mLightSensorEnabled = enable;
-            // clear calling identity so sensor manager battery stats are accurate
-            long identity = Binder.clearCallingIdentity();
+    }
+
+    private final class SettingsObserver extends ContentObserver {
+        public SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            synchronized (mLock) {
+                handleSettingsChangedLocked();
+            }
+        }
+    }
+
+    private final WindowManagerPolicy.ScreenOnListener mScreenOnListener =
+            new WindowManagerPolicy.ScreenOnListener() {
+        @Override
+        public void onScreenOn() {
+        }
+    };
+
+    /**
+     * Handler for asynchronous operations performed by the power manager.
+     */
+    private final class PowerManagerHandler extends Handler {
+        public PowerManagerHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_USER_ACTIVITY_TIMEOUT:
+                    handleUserActivityTimeout();
+                    break;
+                case MSG_SANDMAN:
+                    handleSandman();
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Represents a wake lock that has been acquired by an application.
+     */
+    private final class WakeLock implements IBinder.DeathRecipient {
+        public final IBinder mLock;
+        public int mFlags;
+        public String mTag;
+        public WorkSource mWorkSource;
+        public int mOwnerUid;
+        public int mOwnerPid;
+
+        public WakeLock(IBinder lock, int flags, String tag, WorkSource workSource,
+                int ownerUid, int ownerPid) {
+            mLock = lock;
+            mFlags = flags;
+            mTag = tag;
+            mWorkSource = copyWorkSource(workSource);
+            mOwnerUid = ownerUid;
+            mOwnerPid = ownerPid;
+        }
+
+        @Override
+        public void binderDied() {
+            PowerManagerService.this.handleWakeLockDeath(this);
+        }
+
+        public boolean hasSameProperties(int flags, String tag, WorkSource workSource,
+                int ownerUid, int ownerPid) {
+            return mFlags == flags
+                    && mTag.equals(tag)
+                    && hasSameWorkSource(workSource)
+                    && mOwnerUid == ownerUid
+                    && mOwnerPid == ownerPid;
+        }
+
+        public void updateProperties(int flags, String tag, WorkSource workSource,
+                int ownerUid, int ownerPid) {
+            mFlags = flags;
+            mTag = tag;
+            updateWorkSource(workSource);
+            mOwnerUid = ownerUid;
+            mOwnerPid = ownerPid;
+        }
+
+        public boolean hasSameWorkSource(WorkSource workSource) {
+            return Objects.equal(mWorkSource, workSource);
+        }
+
+        public void updateWorkSource(WorkSource workSource) {
+            mWorkSource = copyWorkSource(workSource);
+        }
+
+        @Override
+        public String toString() {
+            return getLockLevelString()
+                    + " '" + mTag + "'" + getLockFlagsString()
+                    + " (uid=" + mOwnerUid + ", pid=" + mOwnerPid + ", ws=" + mWorkSource + ")";
+        }
+
+        private String getLockLevelString() {
+            switch (mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+                case PowerManager.FULL_WAKE_LOCK:
+                    return "FULL_WAKE_LOCK                ";
+                case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
+                    return "SCREEN_BRIGHT_WAKE_LOCK       ";
+                case PowerManager.SCREEN_DIM_WAKE_LOCK:
+                    return "SCREEN_DIM_WAKE_LOCK          ";
+                case PowerManager.PARTIAL_WAKE_LOCK:
+                    return "PARTIAL_WAKE_LOCK             ";
+                case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
+                    return "PROXIMITY_SCREEN_OFF_WAKE_LOCK";
+                default:
+                    return "???                           ";
+            }
+        }
+
+        private String getLockFlagsString() {
+            String result = "";
+            if ((mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {
+                result += " ACQUIRE_CAUSES_WAKEUP";
+            }
+            if ((mFlags & PowerManager.ON_AFTER_RELEASE) != 0) {
+                result += " ON_AFTER_RELEASE";
+            }
+            return result;
+        }
+    }
+
+    private final class SuspendBlockerImpl implements SuspendBlocker {
+        private final String mName;
+        private int mReferenceCount;
+
+        public SuspendBlockerImpl(String name) {
+            mName = name;
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
             try {
-                if (enable) {
-                    // reset our highest value when reenabling
-                    mHighestLightSensorValue = -1;
-                    // force recompute of backlight values
-                    final int value = (int)mLightSensorValue;
-                    if (value >= 0) {
-                        mLightSensorValue = -1;
-                        handleLightSensorValue(value, true);
-                    }
-                    mSensorManager.registerListener(mLightListener, mLightSensor,
-                            LIGHT_SENSOR_RATE);
-                } else {
-                    mSensorManager.unregisterListener(mLightListener);
-                    mHandler.removeCallbacks(mAutoBrightnessTask);
-                    mLightSensorPendingDecrease = false;
-                    mLightSensorPendingIncrease = false;
+                if (mReferenceCount != 0) {
+                    Log.wtf(TAG, "Suspend blocker \"" + mName
+                            + "\" was finalized without being released!");
+                    mReferenceCount = 0;
+                    nativeReleaseSuspendBlocker(mName);
                 }
             } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-    }
-
-    SensorEventListener mProximityListener = new SensorEventListener() {
-        public void onSensorChanged(SensorEvent event) {
-            long milliseconds = SystemClock.elapsedRealtime();
-            synchronized (mLocks) {
-                float distance = event.values[0];
-                long timeSinceLastEvent = milliseconds - mLastProximityEventTime;
-                mLastProximityEventTime = milliseconds;
-                mHandler.removeCallbacks(mProximityTask);
-                boolean proximityTaskQueued = false;
-
-                // compare against getMaximumRange to support sensors that only return 0 or 1
-                boolean active = (distance >= 0.0 && distance < PROXIMITY_THRESHOLD &&
-                        distance < mProximitySensor.getMaximumRange());
-
-                if (DEBUG_PROXIMITY_SENSOR) {
-                    Slog.d(TAG, "mProximityListener.onSensorChanged active: " + active);
-                }
-                if (timeSinceLastEvent < PROXIMITY_SENSOR_DELAY) {
-                    // enforce delaying atleast PROXIMITY_SENSOR_DELAY before processing
-                    mProximityPendingValue = (active ? 1 : 0);
-                    mHandler.postDelayed(mProximityTask, PROXIMITY_SENSOR_DELAY - timeSinceLastEvent);
-                    proximityTaskQueued = true;
-                } else {
-                    // process the value immediately
-                    mProximityPendingValue = -1;
-                    proximityChangedLocked(active);
-                }
-
-                // update mProximityPartialLock state
-                boolean held = mProximityPartialLock.isHeld();
-                if (!held && proximityTaskQueued) {
-                    // hold wakelock until mProximityTask runs
-                    mProximityPartialLock.acquire();
-                } else if (held && !proximityTaskQueued) {
-                    mProximityPartialLock.release();
-                }
+                super.finalize();
             }
         }
 
-        public void onAccuracyChanged(Sensor sensor, int accuracy) {
-            // ignore
-        }
-    };
-
-    private void handleLightSensorValue(int value, boolean immediate) {
-        long milliseconds = SystemClock.elapsedRealtime();
-        if (mLightSensorValue == -1
-                || milliseconds < mLastScreenOnTime + mLightSensorWarmupTime
-                || mWaitingForFirstLightSensor) {
-            // process the value immediately if screen has just turned on
-            mHandler.removeCallbacks(mAutoBrightnessTask);
-            mLightSensorPendingDecrease = false;
-            mLightSensorPendingIncrease = false;
-            lightSensorChangedLocked(value, immediate);
-        } else {
-            if ((value > mLightSensorValue && mLightSensorPendingDecrease) ||
-                    (value < mLightSensorValue && mLightSensorPendingIncrease) ||
-                    (value == mLightSensorValue) ||
-                    (!mLightSensorPendingDecrease && !mLightSensorPendingIncrease)) {
-                // delay processing to debounce the sensor
-                mHandler.removeCallbacks(mAutoBrightnessTask);
-                mLightSensorPendingDecrease = (value < mLightSensorValue);
-                mLightSensorPendingIncrease = (value > mLightSensorValue);
-                if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) {
-                    mLightSensorPendingValue = value;
-                    mHandler.postDelayed(mAutoBrightnessTask, LIGHT_SENSOR_DELAY);
-                }
-            } else {
-                mLightSensorPendingValue = value;
-            }
-        }
-    }
-
-    SensorEventListener mLightListener = new SensorEventListener() {
         @Override
-        public void onSensorChanged(SensorEvent event) {
-            if (DEBUG_LIGHT_SENSOR) {
-                Slog.d(TAG, "onSensorChanged: light value: " + event.values[0]);
-            }
-            synchronized (mLocks) {
-                // ignore light sensor while screen is turning off
-                if (isScreenTurningOffLocked()) {
-                    return;
-                }
-                handleLightSensorValue((int)event.values[0], mWaitingForFirstLightSensor);
-                if (mWaitingForFirstLightSensor && !mPreparingForScreenOn) {
-                    if (DEBUG_LIGHT_ANIMATION) {
-                        Slog.d(TAG, "onSensorChanged: Clearing mWaitingForFirstLightSensor.");
-                    }
-                    mWaitingForFirstLightSensor = false;
+        public void acquire() {
+            synchronized (this) {
+                mReferenceCount += 1;
+                if (mReferenceCount == 1) {
+                    nativeAcquireSuspendBlocker(mName);
                 }
             }
         }
 
         @Override
-        public void onAccuracyChanged(Sensor sensor, int accuracy) {
-            // ignore
+        public void release() {
+            synchronized (this) {
+                mReferenceCount -= 1;
+                if (mReferenceCount == 0) {
+                    nativeReleaseSuspendBlocker(mName);
+                } else if (mReferenceCount < 0) {
+                    Log.wtf(TAG, "Suspend blocker \"" + mName
+                            + "\" was released without being acquired!", new Throwable());
+                    mReferenceCount = 0;
+                }
+            }
         }
-    };
+
+        @Override
+        public String toString() {
+            synchronized (this) {
+                return mName + ": ref count=" + mReferenceCount;
+            }
+        }
+    }
 }
diff --git a/services/java/com/android/server/power/RampAnimator.java b/services/java/com/android/server/power/RampAnimator.java
new file mode 100644
index 0000000..6f063c3
--- /dev/null
+++ b/services/java/com/android/server/power/RampAnimator.java
@@ -0,0 +1,131 @@
+/*
+ * 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;
+
+import android.animation.ValueAnimator;
+import android.util.IntProperty;
+import android.view.Choreographer;
+
+/**
+ * A custom animator that progressively updates a property value at
+ * a given variable rate until it reaches a particular target value.
+ */
+final class RampAnimator<T> {
+    private final T mObject;
+    private final IntProperty<T> mProperty;
+    private final Choreographer mChoreographer;
+
+    private int mCurrentValue;
+    private int mTargetValue;
+    private int mRate;
+
+    private boolean mAnimating;
+    private float mAnimatedValue; // higher precision copy of mCurrentValue
+    private long mLastFrameTimeNanos;
+
+    private boolean mFirstTime = true;
+
+    public RampAnimator(T object, IntProperty<T> property) {
+        mObject = object;
+        mProperty = property;
+        mChoreographer = Choreographer.getInstance();
+    }
+
+    /**
+     * Starts animating towards the specified value.
+     *
+     * If this is the first time the property is being set, the value jumps
+     * directly to the target.
+     *
+     * @param target The target value.
+     * @param rate The convergence rate, in units per second.
+     * @return True if the target differs from the previous target.
+     */
+    public boolean animateTo(int target, int rate) {
+        // Immediately jump to the target the first time.
+        if (mFirstTime) {
+            mFirstTime = false;
+            mProperty.setValue(mObject, target);
+            mCurrentValue = target;
+            return true;
+        }
+
+        // Adjust the rate based on the closest target.
+        // If a faster rate is specified, then use the new rate so that we converge
+        // more rapidly based on the new request.
+        // If a slower rate is specified, then use the new rate only if the current
+        // value is somewhere in between the new and the old target meaning that
+        // we will be ramping in a different direction to get there.
+        // Otherwise, continue at the previous rate.
+        if (!mAnimating
+                || rate > mRate
+                || (target <= mCurrentValue && mCurrentValue <= mTargetValue)
+                || (mTargetValue <= mCurrentValue && mCurrentValue <= target)) {
+            mRate = rate;
+        }
+
+        final boolean changed = (mTargetValue != target);
+        mTargetValue = target;
+
+        // Start animating.
+        if (!mAnimating && target != mCurrentValue) {
+            mAnimating = true;
+            mAnimatedValue = mCurrentValue;
+            mLastFrameTimeNanos = System.nanoTime();
+            postCallback();
+        }
+
+        return changed;
+    }
+
+    private void postCallback() {
+        mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mCallback, null);
+    }
+
+    private final Runnable mCallback = new Runnable() {
+        @Override // Choreographer callback
+        public void run() {
+            final long frameTimeNanos = mChoreographer.getFrameTimeNanos();
+            final float timeDelta = (frameTimeNanos - mLastFrameTimeNanos)
+                    * 0.000000001f;
+            final float amount = timeDelta * mRate / ValueAnimator.getDurationScale();
+            mLastFrameTimeNanos = frameTimeNanos;
+
+            // Advance the animated value towards the target at the specified rate
+            // and clamp to the target. This gives us the new current value but
+            // we keep the animated value around to allow for fractional increments
+            // towards the target.
+            int oldCurrentValue = mCurrentValue;
+            if (mTargetValue > mCurrentValue) {
+                mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue);
+            } else {
+                mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
+            }
+            mCurrentValue = (int)Math.round(mAnimatedValue);
+
+            if (oldCurrentValue != mCurrentValue) {
+                mProperty.setValue(mObject, mCurrentValue);
+            }
+
+            if (mTargetValue != mCurrentValue) {
+                postCallback();
+            } else {
+                mAnimating = false;
+            }
+        }
+    };
+}
diff --git a/services/java/com/android/server/power/SuspendBlocker.java b/services/java/com/android/server/power/SuspendBlocker.java
new file mode 100644
index 0000000..70b278a
--- /dev/null
+++ b/services/java/com/android/server/power/SuspendBlocker.java
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+/**
+ * Low-level suspend blocker mechanism equivalent to holding a partial wake lock.
+ *
+ * This interface is used internally to avoid introducing internal dependencies
+ * on the high-level wake lock mechanism.
+ */
+interface SuspendBlocker {
+    /**
+     * Acquires the suspend blocker.
+     * Prevents the CPU from going to sleep.
+     *
+     * Calls to acquire() nest and must be matched by the same number
+     * of calls to release().
+     */
+    void acquire();
+
+    /**
+     * Releases the suspend blocker.
+     * Allows the CPU to go to sleep if no other suspend blockers are held.
+     *
+     * It is an error to call release() if the suspend blocker has not been acquired.
+     * The system may crash.
+     */
+    void release();
+}
diff --git a/services/java/com/android/server/usb/UsbSettingsManager.java b/services/java/com/android/server/usb/UsbSettingsManager.java
index 7dde340..9b3459b 100644
--- a/services/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/java/com/android/server/usb/UsbSettingsManager.java
@@ -545,6 +545,10 @@
             defaultPackage = mDevicePreferenceMap.get(new DeviceFilter(device));
         }
 
+        // Send broadcast to running activity with registered intent
+        mContext.sendBroadcast(intent);
+
+        // Start activity with registered intent
         resolveActivity(intent, matches, defaultPackage, device, null);
     }
 
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 680ae4c0..27c298c 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -57,6 +57,7 @@
 import android.app.IActivityManager;
 import android.app.StatusBarManager;
 import android.app.admin.DevicePolicyManager;
+import android.animation.ValueAnimator;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -854,7 +855,7 @@
             android.os.Process.setThreadPriority(
                     android.os.Process.THREAD_PRIORITY_FOREGROUND);
             android.os.Process.setCanSelfBackground(false);
-            mPolicy.init(mContext, mService, mService, mPM);
+            mPolicy.init(mContext, mService, mService);
             mService.mAnimator.mAboveUniverseLayer = mPolicy.getAboveUniverseLayer()
                     * TYPE_LAYER_MULTIPLIER
                     + TYPE_LAYER_OFFSET;
@@ -900,8 +901,8 @@
                 Settings.System.WINDOW_ANIMATION_SCALE, mWindowAnimationScale);
         mTransitionAnimationScale = Settings.System.getFloat(context.getContentResolver(),
                 Settings.System.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale);
-        mAnimatorDurationScale = Settings.System.getFloat(context.getContentResolver(),
-                Settings.System.ANIMATOR_DURATION_SCALE, mTransitionAnimationScale);
+        setAnimatorDurationScale(Settings.System.getFloat(context.getContentResolver(),
+                Settings.System.ANIMATOR_DURATION_SCALE, mTransitionAnimationScale));
 
         // Track changes to DevicePolicyManager state so we can enable/disable keyguard.
         IntentFilter filter = new IntentFilter();
@@ -909,7 +910,7 @@
         mContext.registerReceiver(mBroadcastReceiver, filter);
 
         mHoldingScreenWakeLock = pmc.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK
-                | PowerManager.ON_AFTER_RELEASE, "KEEP_SCREEN_ON_FLAG");
+                | PowerManager.ON_AFTER_RELEASE, PowerManager.KEEP_SCREEN_ON_FLAG_TAG);
         mHoldingScreenWakeLock.setReferenceCounted(false);
 
         mInputManager = new InputManagerService(context, mInputMonitor);
@@ -5160,7 +5161,7 @@
                 mTransitionAnimationScale = fixScale(scales[1]);
             }
             if (scales.length >= 3) {
-                mAnimatorDurationScale = fixScale(scales[2]);
+                setAnimatorDurationScale(fixScale(scales[2]));
             }
         }
 
@@ -5168,6 +5169,11 @@
         mH.obtainMessage(H.PERSIST_ANIMATION_SCALE).sendToTarget();
     }
 
+    private void setAnimatorDurationScale(float scale) {
+        mAnimatorDurationScale = scale;
+        ValueAnimator.setDurationScale(scale);
+    }
+
     public float getAnimationScale(int which) {
         switch (which) {
             case 0: return mWindowAnimationScale;
@@ -5868,7 +5874,7 @@
             }
         }
 
-        rebuildBlackFrame();
+        rebuildBlackFrameLocked();
 
         final WindowList windows = displayContent.getWindowList();
         for (int i = windows.size() - 1; i >= 0; i--) {
@@ -6885,6 +6891,8 @@
         displayReady(Display.DEFAULT_DISPLAY);
 
         synchronized(mWindowMap) {
+            readForcedDisplaySizeAndDensityLocked(getDefaultDisplayContent());
+
             WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
             mDisplay = wm.getDefaultDisplay();
             mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
@@ -6906,6 +6914,11 @@
             mPolicy.setInitialDisplaySize(mDisplay, displayContent.mInitialDisplayWidth,
                     displayContent.mInitialDisplayHeight, displayContent.mInitialDisplayDensity);
         }
+
+        try {
+            mActivityManager.updateConfiguration(null);
+        } catch (RemoteException e) {
+        }
     }
 
     public void displayReady(int displayId) {
@@ -6924,15 +6937,6 @@
                 displayContent.mBaseDisplayDensity = displayContent.mInitialDisplayDensity;
             }
         }
-
-        try {
-            mActivityManager.updateConfiguration(null);
-        } catch (RemoteException e) {
-        }
-
-        synchronized (mWindowMap) {
-            readForcedDisplaySizeAndDensityLocked(getDisplayContent(displayId));
-        }
     }
 
     public void systemReady() {
@@ -7561,7 +7565,7 @@
         }
     }
 
-    private void rebuildBlackFrame() {
+    private void rebuildBlackFrameLocked() {
         if (mBlackFrame != null) {
             mBlackFrame.kill();
             mBlackFrame = null;
@@ -7573,6 +7577,13 @@
             int initW, initH, baseW, baseH;
             final boolean rotated = (mRotation == Surface.ROTATION_90
                     || mRotation == Surface.ROTATION_270);
+            if (DEBUG_BOOT) {
+                Slog.i(TAG, "BLACK FRAME: rotated=" + rotated + " init="
+                        + displayContent.mInitialDisplayWidth + "x"
+                        + displayContent.mInitialDisplayHeight + " base="
+                        + displayContent.mBaseDisplayWidth + "x"
+                        + displayContent.mBaseDisplayHeight);
+            }
             if (rotated) {
                 initW = displayContent.mInitialDisplayHeight;
                 initH = displayContent.mInitialDisplayWidth;
@@ -7634,7 +7645,7 @@
             }
         }
         if (changed) {
-            reconfigureDisplayLocked(displayContent);
+            rebuildBlackFrameLocked();
         }
     }
 
@@ -7663,7 +7674,7 @@
             final DisplayContent displayContent = getDisplayContent(displayId);
             setForcedDisplayDensityLocked(displayContent, density);
             Settings.Secure.putString(mContext.getContentResolver(),
-                    Settings.Secure.DISPLAY_SIZE_FORCED, Integer.toString(density));
+                    Settings.Secure.DISPLAY_DENSITY_FORCED, Integer.toString(density));
         }
     }
 
@@ -7706,7 +7717,7 @@
             mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
         }
 
-        rebuildBlackFrame();
+        rebuildBlackFrameLocked();
 
         performLayoutAndPlaceSurfacesLocked();
     }
@@ -9068,16 +9079,16 @@
         setHoldScreenLocked(mInnerFields.mHoldScreen != null);
         if (!mDisplayFrozen) {
             if (mInnerFields.mScreenBrightness < 0 || mInnerFields.mScreenBrightness > 1.0f) {
-                mPowerManager.setScreenBrightnessOverride(-1);
+                mPowerManager.setScreenBrightnessOverrideFromWindowManager(-1);
             } else {
-                mPowerManager.setScreenBrightnessOverride((int)
-                        (mInnerFields.mScreenBrightness * PowerManager.BRIGHTNESS_ON));
+                mPowerManager.setScreenBrightnessOverrideFromWindowManager(
+                        toBrightnessOverride(mInnerFields.mScreenBrightness));
             }
             if (mInnerFields.mButtonBrightness < 0 || mInnerFields.mButtonBrightness > 1.0f) {
-                mPowerManager.setButtonBrightnessOverride(-1);
+                mPowerManager.setButtonBrightnessOverrideFromWindowManager(-1);
             } else {
-                mPowerManager.setButtonBrightnessOverride((int)
-                        (mInnerFields.mButtonBrightness * PowerManager.BRIGHTNESS_ON));
+                mPowerManager.setButtonBrightnessOverrideFromWindowManager(
+                        toBrightnessOverride(mInnerFields.mButtonBrightness));
             }
         }
         if (mInnerFields.mHoldScreen != mHoldingScreenOn) {
@@ -9088,8 +9099,7 @@
 
         if (mTurnOnScreen) {
             if (DEBUG_VISIBILITY) Slog.v(TAG, "Turning screen on after layout!");
-            mPowerManager.userActivity(SystemClock.uptimeMillis(), false,
-                    PowerManager.USER_ACTIVITY_EVENT_BUTTON, true);
+            mPowerManager.wakeUp(SystemClock.uptimeMillis());
             mTurnOnScreen = false;
         }
 
@@ -9120,6 +9130,10 @@
         }
     }
 
+    private int toBrightnessOverride(float value) {
+        return (int)(value * PowerManager.BRIGHTNESS_ON);
+    }
+
     void checkDrawnWindowsLocked() {
         if (mWaitingForDrawn.size() > 0) {
             for (int j=mWaitingForDrawn.size()-1; j>=0; j--) {
diff --git a/services/jni/com_android_server_LightsService.cpp b/services/jni/com_android_server_LightsService.cpp
index 9ed495195..401e1aa 100644
--- a/services/jni/com_android_server_LightsService.cpp
+++ b/services/jni/com_android_server_LightsService.cpp
@@ -120,7 +120,10 @@
     state.flashOffMS = offMS;
     state.brightnessMode = brightnessMode;
 
-    devices->lights[light]->set_light(devices->lights[light], &state);
+    {
+        ALOGD_IF_SLOW(50, "Excessive delay setting light");
+        devices->lights[light]->set_light(devices->lights[light], &state);
+    }
 }
 
 static JNINativeMethod method_table[] = {
diff --git a/services/jni/com_android_server_input_InputManagerService.cpp b/services/jni/com_android_server_input_InputManagerService.cpp
index 35c2142..701b15a 100644
--- a/services/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/jni/com_android_server_input_InputManagerService.cpp
@@ -143,7 +143,7 @@
 
 enum {
     WM_ACTION_PASS_TO_USER = 1,
-    WM_ACTION_POKE_USER_ACTIVITY = 2,
+    WM_ACTION_WAKE_UP = 2,
     WM_ACTION_GO_TO_SLEEP = 4,
 };
 
@@ -899,11 +899,11 @@
         android_server_PowerManagerService_goToSleep(when);
     }
 
-    if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
+    if (wmActions & WM_ACTION_WAKE_UP) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
-        ALOGD("handleInterceptActions: Poking user activity.");
+        ALOGD("handleInterceptActions: Waking up.");
 #endif
-        android_server_PowerManagerService_userActivity(when, USER_ACTIVITY_EVENT_BUTTON);
+        android_server_PowerManagerService_wakeUp(when);
     }
 
     if (wmActions & WM_ACTION_PASS_TO_USER) {
diff --git a/services/jni/com_android_server_power_PowerManagerService.cpp b/services/jni/com_android_server_power_PowerManagerService.cpp
index 8307d25..3f3970b 100644
--- a/services/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/jni/com_android_server_power_PowerManagerService.cpp
@@ -21,6 +21,8 @@
 #include "JNIHelp.h"
 #include "jni.h"
 
+#include <ScopedUtfChars.h>
+
 #include <limits.h>
 
 #include <android_runtime/AndroidRuntime.h>
@@ -28,6 +30,7 @@
 #include <utils/Timers.h>
 #include <utils/misc.h>
 #include <utils/String8.h>
+#include <utils/Log.h>
 #include <hardware/power.h>
 #include <hardware_legacy/power.h>
 #include <cutils/android_reboot.h>
@@ -42,8 +45,9 @@
 // ----------------------------------------------------------------------------
 
 static struct {
-    jmethodID goToSleep;
-    jmethodID userActivity;
+    jmethodID wakeUpFromNative;
+    jmethodID goToSleepFromNative;
+    jmethodID userActivityFromNative;
 } gPowerManagerServiceClassInfo;
 
 // ----------------------------------------------------------------------------
@@ -106,9 +110,21 @@
 
         JNIEnv* env = AndroidRuntime::getJNIEnv();
 
-        env->CallVoidMethod(gPowerManagerServiceObj, gPowerManagerServiceClassInfo.userActivity,
-                nanoseconds_to_milliseconds(eventTime), false, eventType, false);
-        checkAndClearExceptionFromCallback(env, "userActivity");
+        env->CallVoidMethod(gPowerManagerServiceObj,
+                gPowerManagerServiceClassInfo.userActivityFromNative,
+                nanoseconds_to_milliseconds(eventTime), eventType, 0);
+        checkAndClearExceptionFromCallback(env, "userActivityFromNative");
+    }
+}
+
+void android_server_PowerManagerService_wakeUp(nsecs_t eventTime) {
+    if (gPowerManagerServiceObj) {
+        JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+        env->CallVoidMethod(gPowerManagerServiceObj,
+                gPowerManagerServiceClassInfo.wakeUpFromNative,
+                nanoseconds_to_milliseconds(eventTime));
+        checkAndClearExceptionFromCallback(env, "wakeUpFromNative");
     }
 }
 
@@ -116,9 +132,10 @@
     if (gPowerManagerServiceObj) {
         JNIEnv* env = AndroidRuntime::getJNIEnv();
 
-        env->CallVoidMethod(gPowerManagerServiceObj, gPowerManagerServiceClassInfo.goToSleep,
-                nanoseconds_to_milliseconds(eventTime));
-        checkAndClearExceptionFromCallback(env, "goToSleep");
+        env->CallVoidMethod(gPowerManagerServiceObj,
+                gPowerManagerServiceClassInfo.goToSleepFromNative,
+                nanoseconds_to_milliseconds(eventTime), 0);
+        checkAndClearExceptionFromCallback(env, "goToSleepFromNative");
     }
 }
 
@@ -137,68 +154,62 @@
 }
 
 static void nativeSetPowerState(JNIEnv* env,
-        jobject serviceObj, jboolean screenOn, jboolean screenBright) {
+        jclass clazz, jboolean screenOn, jboolean screenBright) {
     AutoMutex _l(gPowerManagerLock);
     gScreenOn = screenOn;
     gScreenBright = screenBright;
 }
 
-static void nativeStartSurfaceFlingerAnimation(JNIEnv* env,
-        jobject obj, jint mode) {
-    // this is not handled by surfaceflinger anymore
+static void nativeAcquireSuspendBlocker(JNIEnv *env, jclass clazz, jstring nameStr) {
+    ScopedUtfChars name(env, nameStr);
+    acquire_wake_lock(PARTIAL_WAKE_LOCK, name.c_str());
 }
 
-static void nativeAcquireWakeLock(JNIEnv *env, jobject clazz, jint lock, jstring idObj) {
-    if (idObj == NULL) {
-        jniThrowNullPointerException(env, "id is null");
-        return;
-    }
-
-    const char *id = env->GetStringUTFChars(idObj, NULL);
-
-    acquire_wake_lock(lock, id);
-
-    env->ReleaseStringUTFChars(idObj, id);
+static void nativeReleaseSuspendBlocker(JNIEnv *env, jclass clazz, jstring nameStr) {
+    ScopedUtfChars name(env, nameStr);
+    release_wake_lock(name.c_str());
 }
 
-static void nativeReleaseWakeLock(JNIEnv *env, jobject clazz, jstring idObj) {
-    if (idObj == NULL) {
-        jniThrowNullPointerException(env, "id is null");
-        return ;
-    }
-
-    const char *id = env->GetStringUTFChars(idObj, NULL);
-
-    release_wake_lock(id);
-
-    env->ReleaseStringUTFChars(idObj, id);
-
-}
-
-static int nativeSetScreenState(JNIEnv *env, jobject clazz, jboolean on) {
+static void nativeSetScreenState(JNIEnv *env, jclass clazz, jboolean on) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
     if (on) {
-        autosuspend_disable();
+        {
+            ALOGD_IF_SLOW(50, "Excessive delay in autosuspend_disable() while turning screen on");
+            autosuspend_disable();
+        }
+
         if (gPowerModule) {
+            ALOGD_IF_SLOW(10, "Excessive delay in setInteractive(true) while turning screen on");
             gPowerModule->setInteractive(gPowerModule, true);
         }
-        s->unblank();
+
+        {
+            ALOGD_IF_SLOW(20, "Excessive delay in unblank() while turning screen on");
+            s->unblank();
+        }
     } else {
-        s->blank();
+        {
+            ALOGD_IF_SLOW(20, "Excessive delay in blank() while turning screen off");
+            s->blank();
+        }
+
         if (gPowerModule) {
+            ALOGD_IF_SLOW(10, "Excessive delay in setInteractive(false) while turning screen off");
             gPowerModule->setInteractive(gPowerModule, false);
         }
-        autosuspend_enable();
-    }
 
-    return 0;
+        {
+            ALOGD_IF_SLOW(50, "Excessive delay in autosuspend_enable() while turning screen off");
+            autosuspend_enable();
+        }
+    }
 }
 
-static void nativeShutdown(JNIEnv *env, jobject clazz) {
+static void nativeShutdown(JNIEnv *env, jclass clazz) {
     android_reboot(ANDROID_RB_POWEROFF, 0, 0);
 }
 
-static void nativeReboot(JNIEnv *env, jobject clazz, jstring reason) {
+static void nativeReboot(JNIEnv *env, jclass clazz, jstring reason) {
     if (reason == NULL) {
         android_reboot(ANDROID_RB_RESTART, 0, 0);
     } else {
@@ -218,13 +229,11 @@
             (void*) nativeInit },
     { "nativeSetPowerState", "(ZZ)V",
             (void*) nativeSetPowerState },
-    { "nativeStartSurfaceFlingerAnimation", "(I)V",
-            (void*) nativeStartSurfaceFlingerAnimation },
-    { "nativeAcquireWakeLock", "(ILjava/lang/String;)V",
-            (void*) nativeAcquireWakeLock },
-    { "nativeReleaseWakeLock", "(Ljava/lang/String;)V",
-            (void*) nativeReleaseWakeLock },
-    { "nativeSetScreenState", "(Z)I",
+    { "nativeAcquireSuspendBlocker", "(Ljava/lang/String;)V",
+            (void*) nativeAcquireSuspendBlocker },
+    { "nativeReleaseSuspendBlocker", "(Ljava/lang/String;)V",
+            (void*) nativeReleaseSuspendBlocker },
+    { "nativeSetScreenState", "(Z)V",
             (void*) nativeSetScreenState },
     { "nativeShutdown", "()V",
             (void*) nativeShutdown },
@@ -254,11 +263,14 @@
     jclass clazz;
     FIND_CLASS(clazz, "com/android/server/power/PowerManagerService");
 
-    GET_METHOD_ID(gPowerManagerServiceClassInfo.goToSleep, clazz,
-            "goToSleep", "(J)V");
+    GET_METHOD_ID(gPowerManagerServiceClassInfo.wakeUpFromNative, clazz,
+            "wakeUpFromNative", "(J)V");
 
-    GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivity, clazz,
-            "userActivity", "(JZIZ)V");
+    GET_METHOD_ID(gPowerManagerServiceClassInfo.goToSleepFromNative, clazz,
+            "goToSleepFromNative", "(JI)V");
+
+    GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivityFromNative, clazz,
+            "userActivityFromNative", "(JII)V");
 
     // Initialize
     for (int i = 0; i <= USER_ACTIVITY_EVENT_LAST; i++) {
diff --git a/services/jni/com_android_server_power_PowerManagerService.h b/services/jni/com_android_server_power_PowerManagerService.h
index cc3b5ef5..0808b80 100644
--- a/services/jni/com_android_server_power_PowerManagerService.h
+++ b/services/jni/com_android_server_power_PowerManagerService.h
@@ -27,6 +27,7 @@
 extern bool android_server_PowerManagerService_isScreenOn();
 extern bool android_server_PowerManagerService_isScreenBright();
 extern void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType);
+extern void android_server_PowerManagerService_wakeUp(nsecs_t eventTime);
 extern void android_server_PowerManagerService_goToSleep(nsecs_t eventTime);
 
 } // namespace android
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 3f66de6..89b2474 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -33,6 +33,7 @@
     <uses-permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.MANAGE_USERS" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index f6f9aa0..3373fd4 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -71,7 +71,7 @@
 import android.os.INetworkManagementService;
 import android.os.IPowerManager;
 import android.os.MessageQueue.IdleHandler;
-import android.os.UserId;
+import android.os.UserHandle;
 import android.test.AndroidTestCase;
 import android.test.mock.MockPackageManager;
 import android.test.suitebuilder.annotation.LargeTest;
@@ -138,10 +138,10 @@
     private static final int APP_ID_A = android.os.Process.FIRST_APPLICATION_UID + 800;
     private static final int APP_ID_B = android.os.Process.FIRST_APPLICATION_UID + 801;
 
-    private static final int UID_A = UserId.getUid(USER_ID, APP_ID_A);
-    private static final int UID_B = UserId.getUid(USER_ID, APP_ID_B);
-    private static final int UID_A_GUEST = UserId.getUid(USER_ID_GUEST, APP_ID_A);
-    private static final int UID_B_GUEST = UserId.getUid(USER_ID_GUEST, APP_ID_B);
+    private static final int UID_A = UserHandle.getUid(USER_ID, APP_ID_A);
+    private static final int UID_B = UserHandle.getUid(USER_ID, APP_ID_B);
+    private static final int UID_A_GUEST = UserHandle.getUid(USER_ID_GUEST, APP_ID_A);
+    private static final int UID_B_GUEST = UserHandle.getUid(USER_ID_GUEST, APP_ID_B);
 
     private static final int PID_1 = 400;
     private static final int PID_2 = 401;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index bc3649c..59a86c2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -16,35 +16,23 @@
 
 package com.android.server.pm;
 
-import com.android.server.pm.UserManagerService;
-
+import android.content.Context;
 import android.content.pm.UserInfo;
 import android.os.Debug;
 import android.os.Environment;
+import android.os.UserManager;
 import android.test.AndroidTestCase;
 
 import java.util.List;
 
-/** Test {@link UserManagerService} functionality. */
+/** Test {@link UserManager} functionality. */
 public class UserManagerTest extends AndroidTestCase {
 
-    UserManagerService mUserManager = null;
+    UserManager mUserManager = null;
 
     @Override
     public void setUp() throws Exception {
-        mUserManager = new UserManagerService(Environment.getExternalStorageDirectory(),
-                Environment.getExternalStorageDirectory());
-    }
-
-    @Override
-    public void tearDown() throws Exception {
-        List<UserInfo> users = mUserManager.getUsers();
-        // Remove all except the primary user
-        for (UserInfo user : users) {
-            if (!user.isPrimary()) {
-                mUserManager.removeUser(user.id);
-            }
-        }
+        mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
     }
 
     public void testHasPrimary() throws Exception {
@@ -52,12 +40,10 @@
     }
 
     public void testAddUser() throws Exception {
-        final UserManagerService details = mUserManager;
-
-        UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
+        UserInfo userInfo = mUserManager.createUser("Guest 1", UserInfo.FLAG_GUEST);
         assertTrue(userInfo != null);
 
-        List<UserInfo> list = details.getUsers();
+        List<UserInfo> list = mUserManager.getUsers();
         boolean found = false;
         for (UserInfo user : list) {
             if (user.id == userInfo.id && user.name.equals("Guest 1")
@@ -68,13 +54,12 @@
             }
         }
         assertTrue(found);
+        mUserManager.removeUser(userInfo.id);
     }
 
     public void testAdd2Users() throws Exception {
-        final UserManagerService details = mUserManager;
-
-        UserInfo user1 = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
-        UserInfo user2 = details.createUser("User 2", UserInfo.FLAG_ADMIN);
+        UserInfo user1 = mUserManager.createUser("Guest 1", UserInfo.FLAG_GUEST);
+        UserInfo user2 = mUserManager.createUser("User 2", UserInfo.FLAG_ADMIN);
 
         assertTrue(user1 != null);
         assertTrue(user2 != null);
@@ -82,14 +67,14 @@
         assertTrue(findUser(0));
         assertTrue(findUser(user1.id));
         assertTrue(findUser(user2.id));
+        mUserManager.removeUser(user1.id);
+        mUserManager.removeUser(user2.id);
     }
 
     public void testRemoveUser() throws Exception {
-        final UserManagerService details = mUserManager;
+        UserInfo userInfo = mUserManager.createUser("Guest 1", UserInfo.FLAG_GUEST);
 
-        UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
-
-        details.removeUser(userInfo.id);
+        mUserManager.removeUser(userInfo.id);
 
         assertFalse(findUser(userInfo.id));
     }
@@ -104,4 +89,18 @@
         }
         return false;
     }
+
+    public void testSerialNumber() {
+        UserInfo user1 = mUserManager.createUser("User 1", UserInfo.FLAG_RESTRICTED);
+        int serialNumber1 = user1.serialNumber;
+        assertEquals(serialNumber1, mUserManager.getUserSerialNumber(user1.id));
+        assertEquals(user1.id, mUserManager.getUserHandle(serialNumber1));
+        mUserManager.removeUser(user1.id);
+        UserInfo user2 = mUserManager.createUser("User 2", UserInfo.FLAG_RESTRICTED);
+        int serialNumber2 = user2.serialNumber;
+        assertFalse(serialNumber1 == serialNumber2);
+        assertEquals(serialNumber2, mUserManager.getUserSerialNumber(user2.id));
+        assertEquals(user2.id, mUserManager.getUserHandle(serialNumber2));
+        mUserManager.removeUser(user2.id);
+    }
 }
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 677f713..078699f 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -87,6 +87,22 @@
     }
 
     /**
+     * Initialize from the SignalStrength structure.
+     *
+     * @param ss
+     *
+     * @hide
+     */
+    public void initialize(SignalStrength ss, int timingAdvance) {
+        mSignalStrength = ss.getLteSignalStrenght();
+        mRsrp = ss.getLteRsrp();
+        mRsrq = ss.getLteRsrq();
+        mRssnr = ss.getLteRssnr();
+        mCqi = ss.getLteCqi();
+        mTimingAdvance = timingAdvance;
+    }
+
+    /**
      * @hide
      */
     protected void copyFrom(CellSignalStrengthLte s) {
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index d0a2e11..f998935 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -47,7 +47,8 @@
     };
 
     /** @hide */
-    public static final int INVALID_SNR = 0x7FFFFFFF;
+    //Use int max, as -1 is a valid value in signal strength
+    public static final int INVALID = 0x7FFFFFFF;
 
     private int mGsmSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
     private int mGsmBitErrorRate;   // bit error rate (0-7, 99) as defined in TS 27.007 8.5
@@ -95,15 +96,39 @@
         mEvdoDbm = -1;
         mEvdoEcio = -1;
         mEvdoSnr = -1;
-        mLteSignalStrength = -1;
-        mLteRsrp = -1;
-        mLteRsrq = -1;
-        mLteRssnr = INVALID_SNR;
-        mLteCqi = -1;
+        mLteSignalStrength = 99;
+        mLteRsrp = INVALID;
+        mLteRsrq = INVALID;
+        mLteRssnr = INVALID;
+        mLteCqi = INVALID;
         isGsm = true;
     }
 
     /**
+     * This constructor is used to create SignalStrength with default
+     * values and set the isGsmFlag with the value passed in the input
+     *
+     * @param gsmFlag true if Gsm Phone,false if Cdma phone
+     * @return newly created SignalStrength
+     * @hide
+     */
+    public SignalStrength(boolean gsmFlag) {
+        mGsmSignalStrength = 99;
+        mGsmBitErrorRate = -1;
+        mCdmaDbm = -1;
+        mCdmaEcio = -1;
+        mEvdoDbm = -1;
+        mEvdoEcio = -1;
+        mEvdoSnr = -1;
+        mLteSignalStrength = 99;
+        mLteRsrp = INVALID;
+        mLteRsrq = INVALID;
+        mLteRssnr = INVALID;
+        mLteCqi = INVALID;
+        isGsm = gsmFlag;
+    }
+
+    /**
      * Constructor
      *
      * @hide
@@ -112,10 +137,10 @@
             int cdmaDbm, int cdmaEcio,
             int evdoDbm, int evdoEcio, int evdoSnr,
             int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
-            boolean gsm) {
+            boolean gsmFlag) {
         initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
                 evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
-                lteRsrq, lteRssnr, lteCqi, gsm);
+                lteRsrq, lteRssnr, lteCqi, gsmFlag);
     }
 
     /**
@@ -126,10 +151,10 @@
     public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
             int cdmaDbm, int cdmaEcio,
             int evdoDbm, int evdoEcio, int evdoSnr,
-            boolean gsm) {
+            boolean gsmFlag) {
         initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
-                evdoDbm, evdoEcio, evdoSnr, -1, -1,
-                -1, INVALID_SNR, -1, gsm);
+                evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
+                INVALID, INVALID, INVALID, gsmFlag);
     }
 
     /**
@@ -162,8 +187,8 @@
             int evdoDbm, int evdoEcio, int evdoSnr,
             boolean gsm) {
         initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
-                evdoDbm, evdoEcio, evdoSnr, -1, -1,
-                -1, INVALID_SNR, -1, gsm);
+                evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
+                INVALID, INVALID, INVALID, gsm);
     }
 
     /**
@@ -231,6 +256,8 @@
      * @hide
      */
     public SignalStrength(Parcel in) {
+        if (DBG) log("Size of signalstrength parcel:" + in.dataSize());
+
         mGsmSignalStrength = in.readInt();
         mGsmBitErrorRate = in.readInt();
         mCdmaDbm = in.readInt();
@@ -288,7 +315,54 @@
     };
 
     /**
-     * Get the GSM Signal Strength, valid values are (0-31, 99) as defined in TS 27.007 8.5
+     * Validate the individual signal strength fields as per the range
+     * specified in ril.h
+     * Set to invalid any field that is not in the valid range
+     * Cdma, evdo, lte rsrp & rsrq values are sign converted
+     * when received from ril interface
+     *
+     * @return
+     *      Valid values for all signalstrength fields
+     * @hide
+     */
+    public void validateInput() {
+        if (DBG) log("Signal before validate=" + this);
+        // TS 27.007 8.5
+        mGsmSignalStrength = mGsmSignalStrength >= 0 ? mGsmSignalStrength : 99;
+        // BER no change;
+
+        mCdmaDbm = mCdmaDbm > 0 ? -mCdmaDbm : -120;
+        mCdmaEcio = (mCdmaEcio > 0) ? -mCdmaEcio : -160;
+
+        mEvdoDbm = (mEvdoDbm > 0) ? -mEvdoDbm : -120;
+        mEvdoEcio = (mEvdoEcio > 0) ? -mEvdoEcio : -1;
+        mEvdoSnr = ((mEvdoSnr > 0) && (mEvdoSnr <= 8)) ? mEvdoSnr : -1;
+
+        // TS 36.214 Physical Layer Section 5.1.3, TS 36.331 RRC
+        mLteSignalStrength = (mLteSignalStrength >= 0) ? mLteSignalStrength : 99;
+        mLteRsrp = ((mLteRsrp >= 44) && (mLteRsrp <= 140)) ? -mLteRsrp : SignalStrength.INVALID;
+        mLteRsrq = ((mLteRsrq >= 3) && (mLteRsrq <= 20)) ? -mLteRsrq : SignalStrength.INVALID;
+        mLteRssnr = ((mLteRssnr >= -200) && (mLteRssnr <= 300)) ? mLteRssnr
+                : SignalStrength.INVALID;
+        // Cqi no change
+        if (DBG) log("Signal after validate=" + this);
+    }
+
+    /**
+     * @param true - Gsm, Lte phones
+     *        false - Cdma phones
+     *
+     * Used by voice phone to set the isGsm
+     *        flag
+     * @hide
+     */
+    public void setGsm(boolean gsmFlag) {
+        isGsm = gsmFlag;
+    }
+
+    /**
+     * Get the GSM Signal Strength, valid values are (0-31, 99) as defined in TS
+     * 27.007 8.5
      */
     public int getGsmSignalStrength() {
         return this.mGsmSignalStrength;
@@ -336,6 +410,31 @@
         return this.mEvdoSnr;
     }
 
+    /** @hide */
+    public int getLteSignalStrenght() {
+        return mLteSignalStrength;
+    }
+
+    /** @hide */
+    public int getLteRsrp() {
+        return mLteRsrp;
+    }
+
+    /** @hide */
+    public int getLteRsrq() {
+        return mLteRsrq;
+    }
+
+    /** @hide */
+    public int getLteRssnr() {
+        return mLteRssnr;
+    }
+
+    /** @hide */
+    public int getLteCqi() {
+        return mLteCqi;
+    }
+
     /**
      * Get signal level as an int from 0..4
      *
@@ -345,25 +444,19 @@
         int level;
 
         if (isGsm) {
-            // TODO Need solve the discrepancy of invalid values between
-            // RIL_LTE_SignalStrength and here.
-            if ((mLteSignalStrength == -1)
-                    && (mLteRsrp == -1)
-                    && (mLteRsrq == -1)
-                    && (mLteCqi == -1)) {
+            level = getLteLevel();
+            if (level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
                 level = getGsmLevel();
-            } else {
-                level = getLteLevel();
             }
         } else {
             int cdmaLevel = getCdmaLevel();
             int evdoLevel = getEvdoLevel();
             if (evdoLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
                 /* We don't know evdo, use cdma */
-                level = getCdmaLevel();
+                level = cdmaLevel;
             } else if (cdmaLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
                 /* We don't know cdma, use evdo */
-                level = getEvdoLevel();
+                level = evdoLevel;
             } else {
                 /* We know both, use the lowest level */
                 level = cdmaLevel < evdoLevel ? cdmaLevel : evdoLevel;
@@ -381,10 +474,7 @@
     public int getAsuLevel() {
         int asuLevel;
         if (isGsm) {
-            if ((mLteSignalStrength == -1)
-                    && (mLteRsrp == -1)
-                    && (mLteRsrq == -1)
-                    && (mLteCqi == -1)) {
+            if (getLteLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
                 asuLevel = getGsmAsuLevel();
             } else {
                 asuLevel = getLteAsuLevel();
@@ -416,16 +506,17 @@
         int dBm;
 
         if(isGsm()) {
-            if ((mLteSignalStrength == -1)
-                    && (mLteRsrp == -1)
-                    && (mLteRsrq == -1)
-                    && (mLteCqi == -1)) {
+            if (getLteLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
                 dBm = getGsmDbm();
             } else {
                 dBm = getLteDbm();
             }
         } else {
-            dBm = getCdmaDbm();
+            int cdmaDbm = getCdmaDbm();
+            int evdoDbm = getEvdoDbm();
+
+            return (evdoDbm == -120) ? cdmaDbm : ((cdmaDbm == -120) ? evdoDbm
+                    : (cdmaDbm < evdoDbm ? cdmaDbm : evdoDbm));
         }
         if (DBG) log("getDbm=" + dBm);
         return dBm;
@@ -620,34 +711,63 @@
      * @hide
      */
     public int getLteLevel() {
-        int levelLteRsrp = 0;
-        int levelLteRssnr = 0;
+        /*
+         * TS 36.214 Physical Layer Section 5.1.3 TS 36.331 RRC RSSI = received
+         * signal + noise RSRP = reference signal dBm RSRQ = quality of signal
+         * dB= Number of Resource blocksxRSRP/RSSI SNR = gain=signal/noise ratio
+         * = -10log P1/P2 dB
+         */
+        int rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, rsrpIconLevel = -1, snrIconLevel = -1;
 
-        if (mLteRsrp == -1) levelLteRsrp = 0;
-        else if (mLteRsrp >= -95) levelLteRsrp = SIGNAL_STRENGTH_GREAT;
-        else if (mLteRsrp >= -105) levelLteRsrp = SIGNAL_STRENGTH_GOOD;
-        else if (mLteRsrp >= -115) levelLteRsrp = SIGNAL_STRENGTH_MODERATE;
-        else levelLteRsrp = SIGNAL_STRENGTH_POOR;
+        if (mLteRsrp > -44) rsrpIconLevel = -1;
+        else if (mLteRsrp >= -85) rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
+        else if (mLteRsrp >= -95) rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
+        else if (mLteRsrp >= -105) rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
+        else if (mLteRsrp >= -115) rsrpIconLevel = SIGNAL_STRENGTH_POOR;
+        else if (mLteRsrp >= -140) rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
 
-        if (mLteRssnr == INVALID_SNR) levelLteRssnr = 0;
-        else if (mLteRssnr >= 45) levelLteRssnr = SIGNAL_STRENGTH_GREAT;
-        else if (mLteRssnr >= 10) levelLteRssnr = SIGNAL_STRENGTH_GOOD;
-        else if (mLteRssnr >= -30) levelLteRssnr = SIGNAL_STRENGTH_MODERATE;
-        else levelLteRssnr = SIGNAL_STRENGTH_POOR;
+        /*
+         * Values are -200 dB to +300 (SNR*10dB) RS_SNR >= 13.0 dB =>4 bars 4.5
+         * dB <= RS_SNR < 13.0 dB => 3 bars 1.0 dB <= RS_SNR < 4.5 dB => 2 bars
+         * -3.0 dB <= RS_SNR < 1.0 dB 1 bar RS_SNR < -3.0 dB/No Service Antenna
+         * Icon Only
+         */
+        if (mLteRssnr > 300) snrIconLevel = -1;
+        else if (mLteRssnr >= 130) snrIconLevel = SIGNAL_STRENGTH_GREAT;
+        else if (mLteRssnr >= 45) snrIconLevel = SIGNAL_STRENGTH_GOOD;
+        else if (mLteRssnr >= 10) snrIconLevel = SIGNAL_STRENGTH_MODERATE;
+        else if (mLteRssnr >= -30) snrIconLevel = SIGNAL_STRENGTH_POOR;
+        else if (mLteRssnr >= -200)
+            snrIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
 
-        int level;
-        if (mLteRsrp == -1)
-            level = levelLteRssnr;
-        else if (mLteRssnr == INVALID_SNR)
-            level = levelLteRsrp;
-        else
-            level = (levelLteRssnr < levelLteRsrp) ? levelLteRssnr : levelLteRsrp;
+        if (DBG) log("getLTELevel - rsrp:" + mLteRsrp + " snr:" + mLteRssnr + " rsrpIconLevel:"
+                + rsrpIconLevel + " snrIconLevel:" + snrIconLevel);
 
-        if (DBG) log("Lte rsrp level: "+levelLteRsrp
-                + " snr level: " + levelLteRssnr + " level: " + level);
-        return level;
+        /* Choose a measurement type to use for notification */
+        if (snrIconLevel != -1 && rsrpIconLevel != -1) {
+            /*
+             * The number of bars displayed shall be the smaller of the bars
+             * associated with LTE RSRP and the bars associated with the LTE
+             * RS_SNR
+             */
+            return (rsrpIconLevel < snrIconLevel ? rsrpIconLevel : snrIconLevel);
+        }
+
+        if (snrIconLevel != -1) return snrIconLevel;
+
+        if (rsrpIconLevel != -1) return rsrpIconLevel;
+
+        /* Valid values are (0-63, 99) as defined in TS 36.331 */
+        if (mLteSignalStrength > 63) rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        else if (mLteSignalStrength >= 12) rssiIconLevel = SIGNAL_STRENGTH_GREAT;
+        else if (mLteSignalStrength >= 8) rssiIconLevel = SIGNAL_STRENGTH_GOOD;
+        else if (mLteSignalStrength >= 5) rssiIconLevel = SIGNAL_STRENGTH_MODERATE;
+        else if (mLteSignalStrength >= 0) rssiIconLevel = SIGNAL_STRENGTH_POOR;
+        if (DBG) log("getLTELevel - rssi:" + mLteSignalStrength + " rssiIconLevel:"
+                + rssiIconLevel);
+        return rssiIconLevel;
+
     }
-
     /**
      * Get the LTE signal level as an asu value between 0..97, 99 is unknown
      * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
@@ -657,8 +777,20 @@
     public int getLteAsuLevel() {
         int lteAsuLevel = 99;
         int lteDbm = getLteDbm();
-        if (lteDbm <= -140) lteAsuLevel = 0;
-        else if (lteDbm >= -43) lteAsuLevel = 97;
+        /*
+         * 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+         * 0   -140 dBm or less
+         * 1   -139 dBm
+         * 2...96  -138... -44 dBm
+         * 97  -43 dBm or greater
+         * 255 not known or not detectable
+         */
+        /*
+         * validateInput will always give a valid range between -140 t0 -44 as
+         * per ril.h. so RSRP >= -43 & <-140 will fall under asu level 255
+         * and not 97 or 0
+         */
+        if (lteDbm == SignalStrength.INVALID) lteAsuLevel = 255;
         else lteAsuLevel = lteDbm + 140;
         if (DBG) log("Lte Asu level: "+lteAsuLevel);
         return lteAsuLevel;
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 9acffa3..36f2c14 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -28,6 +28,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.DatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
@@ -477,6 +478,11 @@
     }
 
     @Override
+    public Context createConfigurationContext(Configuration overrideConfiguration) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public boolean isRestricted() {
         throw new UnsupportedOperationException();        
     }
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index 0ec1f13..0577dbb 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -40,12 +40,16 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.util.Log;
 
 public class ActivityTestMain extends Activity {
     static final String TAG = "ActivityTest";
 
+    static final String KEY_CONFIGURATION = "configuration";
+
     ActivityManager mAm;
+    Configuration mOverrideConfig;
 
     class BroadcastResultReceiver extends BroadcastReceiver {
         @Override
@@ -111,6 +115,12 @@
         super.onCreate(savedInstanceState);
 
         mAm = (ActivityManager)getSystemService(ACTIVITY_SERVICE);
+        if (savedInstanceState != null) {
+            mOverrideConfig = savedInstanceState.getParcelable(KEY_CONFIGURATION);
+            if (mOverrideConfig != null) {
+                applyOverrideConfiguration(mOverrideConfig);
+            }
+        }
     }
 
     @Override
@@ -182,6 +192,21 @@
                 return true;
             }
         });
+        menu.add("Density!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+            @Override public boolean onMenuItemClick(MenuItem item) {
+                if (mOverrideConfig == null) {
+                    mOverrideConfig = new Configuration();
+                }
+                if (mOverrideConfig.densityDpi == Configuration.DENSITY_DPI_UNDEFINED) {
+                    mOverrideConfig.densityDpi = (getApplicationContext().getResources()
+                            .getConfiguration().densityDpi*2)/3;
+                } else {
+                    mOverrideConfig.densityDpi = Configuration.DENSITY_DPI_UNDEFINED;
+                }
+                recreate();
+                return true;
+            }
+        });
         return true;
     }
 
@@ -191,6 +216,14 @@
         buildUi();
     }
 
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        if (mOverrideConfig != null) {
+            outState.putParcelable(KEY_CONFIGURATION, mOverrideConfig);
+        }
+    }
+
     private View scrollWrap(View view) {
         ScrollView scroller = new ScrollView(this);
         scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT,
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 1857033..c783ad6 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -177,6 +177,15 @@
         </activity>
 
         <activity
+                android:name="GlyphCacheActivity"
+                android:label="_GlyphCache">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
                 android:name="CanvasTextureViewActivity"
                 android:label="_CanvasTextureView">
             <intent-filter>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GlyphCacheActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GlyphCacheActivity.java
new file mode 100644
index 0000000..e89b294
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GlyphCacheActivity.java
@@ -0,0 +1,75 @@
+/*
+ * 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.test.hwui;
+
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import static android.widget.LinearLayout.LayoutParams;
+
+public class GlyphCacheActivity extends Activity {
+
+    private static final String mCharacterSet = "abcdefghijklmnopqrstuvwxyz" +
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "~!@#$%^&*()_+-={}[]:\";'<>?,./";
+    private int mTotalChars = 0;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        ScrollView scrollView = new ScrollView(this);
+        scrollView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
+        LinearLayout layout = new LinearLayout(this);
+        layout.setOrientation(LinearLayout.VERTICAL);
+        layout.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT));
+        scrollView.addView(layout);
+
+        while (mTotalChars < 10000) {
+            layout.addView(createTextView());
+        }
+        setContentView(scrollView);
+    }
+
+    private TextView createTextView() {
+        TextView textview = new TextView(this);
+        textview.setTextSize(6 + (int) (Math.random() * 5) * 10);
+        textview.setTextColor(0xff << 24 | (int) (Math.random() * 255) << 16 |
+                (int) (Math.random() * 255) << 8 | (int) (Math.random() * 255) << 16);
+        textview.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT));
+        int numChars = 5 + (int) (Math.random() * 10);
+        mTotalChars += numChars;
+        textview.setText(createString(numChars));
+
+        return textview;
+    }
+
+    private String createString(int length) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < length; i++) {
+            sb.append(mCharacterSet.charAt((int)(Math.random() * mCharacterSet.length())));
+        }
+        return sb.toString();
+    }
+}
diff --git a/tests/RenderScriptTests/ComputeBenchmark/Android.mk b/tests/RenderScriptTests/ComputeBenchmark/Android.mk
new file mode 100644
index 0000000..8d47e89
--- /dev/null
+++ b/tests/RenderScriptTests/ComputeBenchmark/Android.mk
@@ -0,0 +1,27 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+                   $(call all-renderscript-files-under, src)
+
+LOCAL_PACKAGE_NAME := RsComputeBenchmark
+
+include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/ComputeBenchmark/AndroidManifest.xml b/tests/RenderScriptTests/ComputeBenchmark/AndroidManifest.xml
new file mode 100644
index 0000000..c8fcc17
--- /dev/null
+++ b/tests/RenderScriptTests/ComputeBenchmark/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.rs.computebench">
+
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-sdk android:minSdkVersion="17" />
+    <application android:label="_RS_Compute_Bench">
+        <activity android:name="ComputeBench">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/RenderScriptTests/ComputeBenchmark/res/layout/main.xml b/tests/RenderScriptTests/ComputeBenchmark/res/layout/main.xml
new file mode 100644
index 0000000..9e9dab8
--- /dev/null
+++ b/tests/RenderScriptTests/ComputeBenchmark/res/layout/main.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    <ImageView
+        android:id="@+id/displayin"
+        android:layout_width="320dip"
+        android:layout_height="266dip" />
+
+    <ImageView
+        android:id="@+id/displayout"
+        android:layout_width="320dip"
+        android:layout_height="266dip" />
+
+</LinearLayout>
diff --git a/tests/RenderScriptTests/ComputeBenchmark/src/com/example/android/rs/computebench/Benchmark.java b/tests/RenderScriptTests/ComputeBenchmark/src/com/example/android/rs/computebench/Benchmark.java
new file mode 100644
index 0000000..ec80719
--- /dev/null
+++ b/tests/RenderScriptTests/ComputeBenchmark/src/com/example/android/rs/computebench/Benchmark.java
@@ -0,0 +1,39 @@
+/*
+ * 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.example.android.rs.computebench;
+import android.content.Context;
+import android.content.res.Resources;
+import android.renderscript.*;
+
+public class Benchmark implements Runnable {
+    private final RenderScript mRS;
+    private ScriptC_compute_benchmark mScript;
+
+    public Benchmark(RenderScript rs, Resources res) {
+        mRS = rs;
+        mScript = new ScriptC_compute_benchmark(mRS, res, R.raw.compute_benchmark);
+    }
+
+    public void run() {
+        long t = java.lang.System.currentTimeMillis();
+        mScript.invoke_bench();
+        mRS.finish();
+        t = java.lang.System.currentTimeMillis() - t;
+        android.util.Log.v("ComputeBench", "Total benchmark took " + t + " ms");
+    }
+
+}
diff --git a/tests/RenderScriptTests/ComputeBenchmark/src/com/example/android/rs/computebench/ComputeBench.java b/tests/RenderScriptTests/ComputeBenchmark/src/com/example/android/rs/computebench/ComputeBench.java
new file mode 100644
index 0000000..2d3e843
--- /dev/null
+++ b/tests/RenderScriptTests/ComputeBenchmark/src/com/example/android/rs/computebench/ComputeBench.java
@@ -0,0 +1,37 @@
+/*
+ * 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.example.android.rs.computebench;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.renderscript.RenderScript;
+
+public class ComputeBench extends Activity {
+    private RenderScript mRS;
+    private Benchmark mBenchmark;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        mRS = RenderScript.create(this);
+
+        mBenchmark = new Benchmark(mRS, getResources());
+        mBenchmark.run();
+    }
+}
diff --git a/tests/RenderScriptTests/ComputeBenchmark/src/com/example/android/rs/computebench/compute_benchmark.rs b/tests/RenderScriptTests/ComputeBenchmark/src/com/example/android/rs/computebench/compute_benchmark.rs
new file mode 100644
index 0000000..7b8ec04
--- /dev/null
+++ b/tests/RenderScriptTests/ComputeBenchmark/src/com/example/android/rs/computebench/compute_benchmark.rs
@@ -0,0 +1,408 @@
+// 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.
+
+#pragma version(1)
+#pragma rs java_package_name(com.example.android.rs.computebench)
+
+// Test configuration (accessible from Java)
+uint priming_runs   = 1000000;
+uint timing_runs    = 5000000;
+
+// Reused variables
+
+static volatile int64_t bench_time;
+static float inv_timing_runs;
+
+#define DECL_VAR_SET(prefix)                \
+static volatile float prefix##_f_1 = 1;     \
+static volatile float2 prefix##_f_2 = 1;    \
+static volatile float3 prefix##_f_3 = 1;    \
+static volatile float4 prefix##_f_4 = 1;    \
+static volatile char prefix##_c_1 = 1;      \
+static volatile char2 prefix##_c_2 = 1;     \
+static volatile char3 prefix##_c_3 = 1;     \
+static volatile char4 prefix##_c_4 = 1;     \
+static volatile uchar prefix##_uc_1 = 1;    \
+static volatile uchar2 prefix##_uc_2 = 1;   \
+static volatile uchar3 prefix##_uc_3 = 1;   \
+static volatile uchar4 prefix##_uc_4 = 1;   \
+static volatile short prefix##_s_1 = 1;     \
+static volatile short2 prefix##_s_2 = 1;    \
+static volatile short3 prefix##_s_3 = 1;    \
+static volatile short4 prefix##_s_4 = 1;    \
+static volatile ushort prefix##_us_1 = 1;   \
+static volatile ushort2 prefix##_us_2 = 1;  \
+static volatile ushort3 prefix##_us_3 = 1;  \
+static volatile ushort4 prefix##_us_4 = 1;  \
+static volatile int prefix##_i_1 = 1;       \
+static volatile int2 prefix##_i_2 = 1;      \
+static volatile int3 prefix##_i_3 = 1;      \
+static volatile int4 prefix##_i_4 = 1;      \
+static volatile uint prefix##_ui_1 = 1;     \
+static volatile uint2 prefix##_ui_2 = 1;    \
+static volatile uint3 prefix##_ui_3 = 1;    \
+static volatile uint4 prefix##_ui_4 = 1;    \
+static volatile long prefix##_l_1 = 1;      \
+static volatile long2 prefix##_l_2 = 1;     \
+static volatile long3 prefix##_l_3 = 1;     \
+static volatile long4 prefix##_l_4 = 1;     \
+static volatile ulong prefix##_ul_1 = 1;    \
+static volatile ulong2 prefix##_ul_2 = 1;   \
+static volatile ulong3 prefix##_ul_3 = 1;   \
+static volatile ulong4 prefix##_ul_4 = 1;   \
+
+DECL_VAR_SET(res)
+DECL_VAR_SET(src1)
+DECL_VAR_SET(src2)
+DECL_VAR_SET(src3)
+
+
+// Testing macros
+
+#define RUN_BENCH(line, op)                         \
+    for (int i = priming_runs - 1; i >= 0; --i) {   \
+        line;                                       \
+    }                                               \
+    bench_time = rsUptimeMillis();                  \
+    for (int i = timing_runs - 1; i >= 0; --i) {    \
+        line;                                       \
+    }                                               \
+    bench_time = rsUptimeMillis() - bench_time;     \
+    rsDebug("    " op " took ns", (float)bench_time * inv_timing_runs);
+
+#define BENCH_BASIC_OP_TYPE(op, type)                                                               \
+    RUN_BENCH(res_##type##_1 = src1_##type##_1 op src2_##type##_1, #type "1 " #op " " #type "1")    \
+    RUN_BENCH(res_##type##_2 = src1_##type##_2 op src2_##type##_2, #type "2 " #op " " #type "2")    \
+    RUN_BENCH(res_##type##_3 = src1_##type##_3 op src2_##type##_3, #type "3 " #op " " #type "3")    \
+    RUN_BENCH(res_##type##_4 = src1_##type##_4 op src2_##type##_4, #type "4 " #op " " #type "4")    \
+
+#define BENCH_BASIC_INT_OP(op)                                  \
+    rsDebug("Testing basic operation " #op, 0);                 \
+    BENCH_BASIC_OP_TYPE(op, c)                                  \
+    BENCH_BASIC_OP_TYPE(op, uc)                                 \
+    BENCH_BASIC_OP_TYPE(op, s)                                  \
+    BENCH_BASIC_OP_TYPE(op, us)                                 \
+    BENCH_BASIC_OP_TYPE(op, i)                                  \
+    BENCH_BASIC_OP_TYPE(op, ui)                                 \
+    RUN_BENCH(res_l_1 = src1_l_1 op src2_l_1, "l1 " #op " l1")  \
+    RUN_BENCH(res_ul_1 = src1_ul_1 op src2_ul_1, "ul1 " #op " ul1")
+
+#define BENCH_BASIC_OP(op)      \
+    BENCH_BASIC_INT_OP(op)      \
+    BENCH_BASIC_OP_TYPE(op, f)
+
+#define BENCH_CVT(to, from, type)                                                                           \
+    rsDebug("Testing convert from " #from " to " #to, 0);                                                   \
+    RUN_BENCH(res_##to##_1 = (type)src1_##from##_1, "(" #to ")" #from)                                      \
+    RUN_BENCH(res_##to##_2 = convert_##type##2(src1_##from##_2), #to "2 convert_" #type "2(" #from "2)")    \
+    RUN_BENCH(res_##to##_3 = convert_##type##3(src1_##from##_3), #to "3 convert_" #type "3(" #from "3)")    \
+    RUN_BENCH(res_##to##_4 = convert_##type##4(src1_##from##_4), #to "4 convert_" #type "4(" #from "4)")
+
+#define BENCH_CVT_MATRIX(to, type)  \
+    BENCH_CVT(to, c, type);         \
+    BENCH_CVT(to, uc, type);        \
+    BENCH_CVT(to, s, type);         \
+    BENCH_CVT(to, us, type);        \
+    BENCH_CVT(to, i, type);         \
+    BENCH_CVT(to, ui, type);        \
+    BENCH_CVT(to, f, type);         \
+
+#define BENCH_XN_FUNC_YN(typeout, fnc, typein)                                                  \
+    RUN_BENCH(res_##typeout##_1 = fnc(src1_##typein##_1);, #typeout "1 " #fnc "(" #typein "1)") \
+    RUN_BENCH(res_##typeout##_2 = fnc(src1_##typein##_2);, #typeout "2 " #fnc "(" #typein "2)") \
+    RUN_BENCH(res_##typeout##_3 = fnc(src1_##typein##_3);, #typeout "3 " #fnc "(" #typein "3)") \
+    RUN_BENCH(res_##typeout##_4 = fnc(src1_##typein##_4);, #typeout "4 " #fnc "(" #typein "4)")
+
+#define BENCH_XN_FUNC_XN_XN(type, fnc)                                                                              \
+    RUN_BENCH(res_##type##_1 = fnc(src1_##type##_1, src2_##type##_1), #type "1 " #fnc "(" #type "1, " #type "1)")   \
+    RUN_BENCH(res_##type##_2 = fnc(src1_##type##_2, src2_##type##_2), #type "2 " #fnc "(" #type "2, " #type "2)")   \
+    RUN_BENCH(res_##type##_3 = fnc(src1_##type##_3, src2_##type##_3), #type "3 " #fnc "(" #type "3, " #type "3)")   \
+    RUN_BENCH(res_##type##_4 = fnc(src1_##type##_4, src2_##type##_4), #type "4 " #fnc "(" #type "4, " #type "4)")   \
+
+#define BENCH_X_FUNC_X_X_X(type, fnc)   \
+    RUN_BENCH(res_##type##_1 = fnc(src1_##type##_1, src2_##type##_1, src3_##type##_1), #type "1 " #fnc "(" #type "1, " #type "1, " #type "1)")
+
+#define BENCH_IN_FUNC_IN(fnc)       \
+    rsDebug("Testing " #fnc, 0);    \
+    BENCH_XN_FUNC_YN(uc, fnc, uc)   \
+    BENCH_XN_FUNC_YN(c, fnc, c)     \
+    BENCH_XN_FUNC_YN(us, fnc, us)   \
+    BENCH_XN_FUNC_YN(s, fnc, s)     \
+    BENCH_XN_FUNC_YN(ui, fnc, ui)   \
+    BENCH_XN_FUNC_YN(i, fnc, i)
+
+#define BENCH_UIN_FUNC_IN(fnc)      \
+    rsDebug("Testing " #fnc, 0);    \
+    BENCH_XN_FUNC_YN(uc, fnc, c)    \
+    BENCH_XN_FUNC_YN(us, fnc, s)    \
+    BENCH_XN_FUNC_YN(ui, fnc, i)    \
+
+#define BENCH_IN_FUNC_IN_IN(fnc)    \
+    rsDebug("Testing " #fnc, 0);    \
+    BENCH_XN_FUNC_XN_XN(uc, fnc)    \
+    BENCH_XN_FUNC_XN_XN(c, fnc)     \
+    BENCH_XN_FUNC_XN_XN(us, fnc)    \
+    BENCH_XN_FUNC_XN_XN(s, fnc)     \
+    BENCH_XN_FUNC_XN_XN(ui, fnc)    \
+    BENCH_XN_FUNC_XN_XN(i, fnc)
+
+#define BENCH_I_FUNC_I_I_I(fnc)     \
+    rsDebug("Testing " #fnc, 0);    \
+    BENCH_X_FUNC_X_X_X(uc, fnc)     \
+    BENCH_X_FUNC_X_X_X(c, fnc)      \
+    BENCH_X_FUNC_X_X_X(us, fnc)     \
+    BENCH_X_FUNC_X_X_X(s, fnc)      \
+    BENCH_X_FUNC_X_X_X(ui, fnc)     \
+    BENCH_X_FUNC_X_X_X(i, fnc)
+
+#define BENCH_FN_FUNC_FN(fnc)                               \
+    rsDebug("Testing " #fnc, 0);                            \
+    RUN_BENCH(res_f_1 = fnc(src1_f_1), "f1 " #fnc "(f1)")   \
+    RUN_BENCH(res_f_2 = fnc(src1_f_2), "f2 " #fnc "(f2)")   \
+    RUN_BENCH(res_f_3 = fnc(src1_f_3), "f3 " #fnc "(f3)")   \
+    RUN_BENCH(res_f_4 = fnc(src1_f_4), "f4 " #fnc "(f4)")
+
+#define BENCH_FN_FUNC_FN_PFN(fnc)                                                   \
+    rsDebug("Testing " #fnc, 0);                                                    \
+    RUN_BENCH(res_f_1 = fnc(src1_f_1, (float*) &src2_f_1), "f1 " #fnc "(f1, f1*)")  \
+    RUN_BENCH(res_f_2 = fnc(src1_f_2, (float2*) &src2_f_2), "f2 " #fnc "(f2, f2*)") \
+    RUN_BENCH(res_f_3 = fnc(src1_f_3, (float3*) &src2_f_3), "f3 " #fnc "(f3, f3*)") \
+    RUN_BENCH(res_f_4 = fnc(src1_f_4, (float4*) &src2_f_4), "f4 " #fnc "(f4, f4*)")
+
+#define BENCH_FN_FUNC_FN_FN(fnc)                                        \
+    rsDebug("Testing " #fnc, 0);                                        \
+    RUN_BENCH(res_f_1 = fnc(src1_f_1, src2_f_1), "f1 " #fnc "(f1, f1)") \
+    RUN_BENCH(res_f_2 = fnc(src1_f_2, src2_f_2), "f2 " #fnc "(f2, f2)") \
+    RUN_BENCH(res_f_3 = fnc(src1_f_3, src2_f_3), "f3 " #fnc "(f3, f3)") \
+    RUN_BENCH(res_f_4 = fnc(src1_f_4, src2_f_4), "f4 " #fnc "(f4, f4)")
+
+#define BENCH_F34_FUNC_F34_F34(fnc)                                     \
+    rsDebug("Testing " #fnc, 0);                                        \
+    RUN_BENCH(res_f_3 = fnc(src1_f_3, src2_f_3), "f3 " #fnc "(f3, f3)") \
+    RUN_BENCH(res_f_4 = fnc(src1_f_4, src2_f_4), "f4 " #fnc "(f4, f4)")
+
+#define BENCH_FN_FUNC_FN_F(fnc)                                         \
+    rsDebug("Testing " #fnc, 0);                                        \
+    RUN_BENCH(res_f_1 = fnc(src1_f_1, src2_f_1), "f1 " #fnc "(f1, f1)") \
+    RUN_BENCH(res_f_2 = fnc(src1_f_2, src2_f_1), "f2 " #fnc "(f2, f1)") \
+    RUN_BENCH(res_f_3 = fnc(src1_f_3, src2_f_1), "f3 " #fnc "(f3, f1)") \
+    RUN_BENCH(res_f_4 = fnc(src1_f_4, src2_f_1), "f4 " #fnc "(f4, f1)")
+
+#define BENCH_F_FUNC_FN(fnc)                                \
+    rsDebug("Testing " #fnc, 0);                            \
+    RUN_BENCH(res_f_1 = fnc(src1_f_1), "f1 " #fnc "(f1)")   \
+    RUN_BENCH(res_f_1 = fnc(src1_f_2), "f1 " #fnc "(f2)")   \
+    RUN_BENCH(res_f_1 = fnc(src1_f_3), "f1 " #fnc "(f3)")   \
+    RUN_BENCH(res_f_1 = fnc(src1_f_4), "f1 " #fnc "(f4)")
+
+#define BENCH_F_FUNC_FN_FN(fnc)                                         \
+    rsDebug("Testing " #fnc, 0);                                        \
+    RUN_BENCH(res_f_1 = fnc(src1_f_1, src2_f_1), "f1 " #fnc "(f1, f1)") \
+    RUN_BENCH(res_f_1 = fnc(src1_f_2, src2_f_2), "f1 " #fnc "(f2, f2)") \
+    RUN_BENCH(res_f_1 = fnc(src1_f_3, src2_f_3), "f1 " #fnc "(f3, f3)") \
+    RUN_BENCH(res_f_1 = fnc(src1_f_4, src2_f_4), "f1 " #fnc "(f4, f4)")
+
+#define BENCH_FN_FUNC_FN_IN(fnc)                                        \
+    rsDebug("Testing " #fnc, 0);                                        \
+    RUN_BENCH(res_f_1 = fnc(src1_f_1, src1_i_1), "f1 " #fnc "(f1, i1)") \
+    RUN_BENCH(res_f_2 = fnc(src1_f_2, src1_i_2), "f2 " #fnc "(f2, i2)") \
+    RUN_BENCH(res_f_3 = fnc(src1_f_3, src1_i_3), "f3 " #fnc "(f3, i3)") \
+    RUN_BENCH(res_f_4 = fnc(src1_f_4, src1_i_4), "f4 " #fnc "(f4, i4)")
+
+#define BENCH_FN_FUNC_FN_I(fnc)                                         \
+    rsDebug("Testing " #fnc, 0);                                        \
+    RUN_BENCH(res_f_1 = fnc(src1_f_1, src1_i_1), "f1 " #fnc "(f1, i1)") \
+    RUN_BENCH(res_f_2 = fnc(src1_f_2, src1_i_1), "f2 " #fnc "(f2, i1)") \
+    RUN_BENCH(res_f_3 = fnc(src1_f_3, src1_i_1), "f3 " #fnc "(f3, i1)") \
+    RUN_BENCH(res_f_4 = fnc(src1_f_4, src1_i_1), "f4 " #fnc "(f4, i1)")
+
+#define BENCH_FN_FUNC_FN_FN_FN(fnc)                                                     \
+    rsDebug("Testing " #fnc, 0);                                                        \
+    RUN_BENCH(res_f_1 = fnc(src1_f_1, src2_f_1, src3_f_1), "f1 " #fnc "(f1, f1, f1)")   \
+    RUN_BENCH(res_f_2 = fnc(src1_f_2, src2_f_2, src3_f_2), "f2 " #fnc "(f2, f2, f2)")   \
+    RUN_BENCH(res_f_3 = fnc(src1_f_3, src2_f_3, src3_f_3), "f3 " #fnc "(f3, f3, f3)")   \
+    RUN_BENCH(res_f_4 = fnc(src1_f_4, src2_f_4, src3_f_4), "f4 " #fnc "(f4, f4, f4)")
+
+#define BENCH_FN_FUNC_FN_FN_F(fnc)                                                      \
+    rsDebug("Testing " #fnc, 0);                                                        \
+    RUN_BENCH(res_f_1 = fnc(src1_f_1, src2_f_1, src3_f_1), "f1 " #fnc "(f1, f1, f1)")   \
+    RUN_BENCH(res_f_2 = fnc(src1_f_2, src2_f_2, src3_f_1), "f2 " #fnc "(f2, f2, f1)")   \
+    RUN_BENCH(res_f_3 = fnc(src1_f_3, src2_f_3, src3_f_1), "f3 " #fnc "(f3, f3, f1)")   \
+    RUN_BENCH(res_f_4 = fnc(src1_f_4, src2_f_4, src3_f_1), "f4 " #fnc "(f4, f4, f1)")
+
+#define BENCH_FN_FUNC_FN_PIN(fnc)                                                   \
+    rsDebug("Testing " #fnc, 0);                                                    \
+    RUN_BENCH(res_f_1 = fnc(src1_f_1, (int*) &src1_i_1), "f1 " #fnc "(f1, i1*)")    \
+    RUN_BENCH(res_f_2 = fnc(src1_f_2, (int2*) &src1_i_2), "f2 " #fnc "(f2, i2*)")   \
+    RUN_BENCH(res_f_3 = fnc(src1_f_3, (int3*) &src1_i_3), "f3 " #fnc "(f3, i3*)")   \
+    RUN_BENCH(res_f_4 = fnc(src1_f_4, (int4*) &src1_i_4), "f4 " #fnc "(f4, i4*)")
+
+#define BENCH_FN_FUNC_FN_FN_PIN(fnc)                                                            \
+    rsDebug("Testing " #fnc, 0);                                                                \
+    RUN_BENCH(res_f_1 = fnc(src1_f_1, src2_f_1, (int*) &src1_i_1), "f1 " #fnc "(f1, f1, i1*)")  \
+    RUN_BENCH(res_f_2 = fnc(src1_f_2, src2_f_2, (int2*) &src1_i_2), "f2 " #fnc "(f2, f2, i2*)") \
+    RUN_BENCH(res_f_3 = fnc(src1_f_3, src2_f_3, (int3*) &src1_i_3), "f3 " #fnc "(f3, f3, i3*)") \
+    RUN_BENCH(res_f_4 = fnc(src1_f_4, src2_f_4, (int4*) &src1_i_4), "f4 " #fnc "(f4, f4, i4*)")
+
+#define BENCH_IN_FUNC_FN(fnc)                               \
+    rsDebug("Testing " #fnc, 0);                            \
+    RUN_BENCH(res_i_1 = fnc(src1_f_1), "i1 " #fnc "(f1)")   \
+    RUN_BENCH(res_i_2 = fnc(src1_f_2), "i2 " #fnc "(f2)")   \
+    RUN_BENCH(res_i_3 = fnc(src1_f_3), "i3 " #fnc "(f3)")   \
+    RUN_BENCH(res_i_4 = fnc(src1_f_4), "i4 " #fnc "(f4)")
+
+
+// Testing functions
+
+static void bench_basic_operators() {
+    int i = 0;
+    BENCH_BASIC_OP(+);
+    BENCH_BASIC_OP(-);
+    BENCH_BASIC_OP(*);
+    BENCH_BASIC_OP(/);
+    BENCH_BASIC_INT_OP(%);
+    BENCH_BASIC_INT_OP(<<);
+    BENCH_BASIC_INT_OP(>>);
+}
+
+static void bench_convert() {
+    BENCH_CVT_MATRIX(c, char);
+    BENCH_CVT_MATRIX(uc, uchar);
+    BENCH_CVT_MATRIX(s, short);
+    BENCH_CVT_MATRIX(us, ushort);
+    BENCH_CVT_MATRIX(i, int);
+    BENCH_CVT_MATRIX(ui, uint);
+    BENCH_CVT_MATRIX(f, float);
+}
+
+static void bench_int_math() {
+    BENCH_UIN_FUNC_IN(abs);
+    BENCH_IN_FUNC_IN(clz);
+    BENCH_IN_FUNC_IN_IN(min);
+    BENCH_IN_FUNC_IN_IN(max);
+    BENCH_I_FUNC_I_I_I(rsClamp);
+}
+
+static void bench_fp_math() {
+    BENCH_FN_FUNC_FN(acos);
+    BENCH_FN_FUNC_FN(acosh);
+    BENCH_FN_FUNC_FN(acospi);
+    BENCH_FN_FUNC_FN(asin);
+    BENCH_FN_FUNC_FN(asinh);
+    BENCH_FN_FUNC_FN(asinpi);
+    BENCH_FN_FUNC_FN(atan);
+    BENCH_FN_FUNC_FN_FN(atan2);
+    BENCH_FN_FUNC_FN(atanh);
+    BENCH_FN_FUNC_FN(atanpi);
+    BENCH_FN_FUNC_FN_FN(atan2pi);
+    BENCH_FN_FUNC_FN(cbrt);
+    BENCH_FN_FUNC_FN(ceil);
+    BENCH_FN_FUNC_FN_FN_FN(clamp);
+    BENCH_FN_FUNC_FN_FN_F(clamp);
+    BENCH_FN_FUNC_FN_FN(copysign);
+    BENCH_FN_FUNC_FN(cos);
+    BENCH_FN_FUNC_FN(cosh);
+    BENCH_FN_FUNC_FN(cospi);
+    BENCH_F34_FUNC_F34_F34(cross);
+    BENCH_FN_FUNC_FN(degrees);
+    BENCH_F_FUNC_FN_FN(distance);
+    BENCH_F_FUNC_FN_FN(dot);
+    BENCH_FN_FUNC_FN(erfc);
+    BENCH_FN_FUNC_FN(erf);
+    BENCH_FN_FUNC_FN(exp);
+    BENCH_FN_FUNC_FN(exp2);
+    BENCH_FN_FUNC_FN(exp10);
+    BENCH_FN_FUNC_FN(expm1);
+    BENCH_FN_FUNC_FN(fabs);
+    BENCH_FN_FUNC_FN_FN(fdim);
+    BENCH_FN_FUNC_FN(floor);
+    BENCH_FN_FUNC_FN_FN_FN(fma);
+    BENCH_FN_FUNC_FN_FN(fmax);
+    BENCH_FN_FUNC_FN_F(fmax);
+    BENCH_FN_FUNC_FN_FN(fmin);
+    BENCH_FN_FUNC_FN_F(fmin);
+    BENCH_FN_FUNC_FN_FN(fmod);
+    BENCH_FN_FUNC_FN_PFN(fract);
+    BENCH_FN_FUNC_FN_PIN(frexp);
+    BENCH_FN_FUNC_FN_FN(hypot);
+    BENCH_IN_FUNC_FN(ilogb);
+    BENCH_FN_FUNC_FN_IN(ldexp);
+    BENCH_FN_FUNC_FN_I(ldexp);
+    BENCH_F_FUNC_FN(length);
+    BENCH_FN_FUNC_FN(lgamma);
+    BENCH_FN_FUNC_FN_PIN(lgamma);
+    BENCH_FN_FUNC_FN(log);
+    BENCH_FN_FUNC_FN(log2);
+    BENCH_FN_FUNC_FN(log10);
+    BENCH_FN_FUNC_FN(log1p);
+    BENCH_FN_FUNC_FN(logb);
+    BENCH_FN_FUNC_FN_FN_FN(mad);
+    BENCH_FN_FUNC_FN_FN(max);
+    BENCH_FN_FUNC_FN_F(max);
+    BENCH_FN_FUNC_FN_FN(min);
+    BENCH_FN_FUNC_FN_F(min);
+    BENCH_FN_FUNC_FN_FN_FN(mix);
+    BENCH_FN_FUNC_FN_FN_F(mix);
+    BENCH_FN_FUNC_FN_PFN(modf);
+    BENCH_FN_FUNC_FN_FN(nextafter);
+    BENCH_FN_FUNC_FN(normalize);
+    BENCH_FN_FUNC_FN_FN(pow);
+    BENCH_FN_FUNC_FN_IN(pown);
+    BENCH_FN_FUNC_FN_FN(powr);
+    BENCH_FN_FUNC_FN(radians);
+    BENCH_FN_FUNC_FN_FN(remainder);
+    BENCH_FN_FUNC_FN_FN_PIN(remquo);
+    BENCH_FN_FUNC_FN(rint);
+    BENCH_FN_FUNC_FN_IN(rootn);
+    BENCH_FN_FUNC_FN(round);
+    BENCH_FN_FUNC_FN(rsqrt);
+    BENCH_FN_FUNC_FN(sign);
+    BENCH_FN_FUNC_FN(sin);
+    BENCH_FN_FUNC_FN_PFN(sincos);
+    BENCH_FN_FUNC_FN(sinh);
+    BENCH_FN_FUNC_FN(sinpi);
+    BENCH_FN_FUNC_FN(sqrt);
+    BENCH_FN_FUNC_FN_FN(step);
+    BENCH_FN_FUNC_FN_F(step);
+    BENCH_FN_FUNC_FN(tan);
+    BENCH_FN_FUNC_FN(tanh);
+    BENCH_FN_FUNC_FN(tanpi);
+    BENCH_FN_FUNC_FN(tgamma);
+    BENCH_FN_FUNC_FN(trunc);
+}
+
+static void bench_approx_math() {
+    BENCH_FN_FUNC_FN(approx_recip);
+    BENCH_FN_FUNC_FN(approx_sqrt);
+    BENCH_FN_FUNC_FN(approx_rsqrt);
+    BENCH_FN_FUNC_FN(approx_length);
+    BENCH_FN_FUNC_FN_FN(approx_distance);
+    BENCH_FN_FUNC_FN(approx_normalize);
+    BENCH_FN_FUNC_FN(approx_atan);
+}
+
+void bench() {
+    rsDebug("RS Compute Benchmark", 0);
+    rsDebug("Current configuration:", 0);
+    rsDebug("Priming runs", priming_runs);
+    rsDebug("Timing runs", timing_runs);
+    rsDebug("Beginning test", 0);
+    inv_timing_runs = 1000000.f / (float)timing_runs;
+    bench_basic_operators();
+    bench_convert();
+    bench_int_math();
+    bench_fp_math();
+    bench_approx_math();
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Fisheye.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Fisheye.java
index bf68f91..81868b10 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Fisheye.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Fisheye.java
@@ -26,12 +26,16 @@
 public class Fisheye extends TestBase {
     private ScriptC_fisheye_full mScript_full = null;
     private ScriptC_fisheye_relaxed mScript_relaxed = null;
+    private ScriptC_fisheye_approx_full mScript_approx_full = null;
+    private ScriptC_fisheye_approx_relaxed mScript_approx_relaxed = null;
+    private final boolean approx;
     private final boolean relaxed;
     private float center_x = 0.5f;
     private float center_y = 0.5f;
     private float scale = 0.5f;
 
-    public Fisheye(boolean relaxed) {
+    public Fisheye(boolean approx, boolean relaxed) {
+        this.approx = approx;
         this.relaxed = relaxed;
     }
 
@@ -68,7 +72,18 @@
     }
 
     private void do_init() {
-        if (relaxed)
+        if (approx) {
+            if (relaxed)
+                mScript_approx_relaxed.invoke_init_filter(
+                        mInPixelsAllocation.getType().getX(),
+                        mInPixelsAllocation.getType().getY(), center_x,
+                        center_y, scale);
+            else
+                mScript_approx_full.invoke_init_filter(
+                        mInPixelsAllocation.getType().getX(),
+                        mInPixelsAllocation.getType().getY(), center_x,
+                        center_y, scale);
+        } else if (relaxed)
             mScript_relaxed.invoke_init_filter(
                     mInPixelsAllocation.getType().getX(),
                     mInPixelsAllocation.getType().getY(), center_x, center_y,
@@ -81,7 +96,19 @@
     }
 
     public void createTest(android.content.res.Resources res) {
-        if (relaxed) {
+        if (approx) {
+            if (relaxed) {
+                mScript_approx_relaxed = new ScriptC_fisheye_approx_relaxed(mRS,
+                        res, R.raw.fisheye_approx_relaxed);
+                mScript_approx_relaxed.set_in_alloc(mInPixelsAllocation);
+                mScript_approx_relaxed.set_sampler(Sampler.CLAMP_LINEAR(mRS));
+            } else {
+                mScript_approx_full = new ScriptC_fisheye_approx_full(mRS, res,
+                        R.raw.fisheye_approx_full);
+                mScript_approx_full.set_in_alloc(mInPixelsAllocation);
+                mScript_approx_full.set_sampler(Sampler.CLAMP_LINEAR(mRS));
+            }
+        } else if (relaxed) {
             mScript_relaxed = new ScriptC_fisheye_relaxed(mRS, res,
                     R.raw.fisheye_relaxed);
             mScript_relaxed.set_in_alloc(mInPixelsAllocation);
@@ -96,7 +123,12 @@
     }
 
     public void runTest() {
-        if (relaxed)
+        if (approx) {
+            if (relaxed)
+                mScript_approx_relaxed.forEach_root(mOutPixelsAllocation);
+            else
+                mScript_approx_full.forEach_root(mOutPixelsAllocation);
+        } else if (relaxed)
             mScript_relaxed.forEach_root(mOutPixelsAllocation);
         else
             mScript_full.forEach_root(mOutPixelsAllocation);
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index 7cd485e..07626a3 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -144,29 +144,38 @@
             mTest = new Grain();
             break;
         case 7:
-            mTest = new Fisheye(false);
+            mTest = new Fisheye(false, false);
             break;
         case 8:
-            mTest = new Fisheye(true);
+            mTest = new Fisheye(false, true);
             break;
         case 9:
-            mTest = new Vignette(false, false);
+            mTest = new Fisheye(true, false);
             break;
         case 10:
-            mTest = new Vignette(false, true);
+            mTest = new Fisheye(true, true);
             break;
         case 11:
-            mTest = new Vignette(true, false);
+            mTest = new Vignette(false, false);
             break;
         case 12:
-            mTest = new Vignette(true, true);
+            mTest = new Vignette(false, true);
             break;
         case 13:
-            mTest = new GroupTest(true);
+            mTest = new Vignette(true, false);
             break;
         case 14:
+            mTest = new Vignette(true, true);
+            break;
+        case 15:
+            mTest = new GroupTest(true);
+            break;
+        case 16:
             mTest = new GroupTest(false);
             break;
+        case 17:
+            mTest = new Intrinsics(0);
+            break;
         }
 
         mTest.createBaseTest(this, mBitmapIn);
@@ -179,7 +188,7 @@
     }
 
     void setupTests() {
-        mTestNames = new String[15];
+        mTestNames = new String[18];
         mTestNames[0] = "Levels Vec3 Relaxed";
         mTestNames[1] = "Levels Vec4 Relaxed";
         mTestNames[2] = "Levels Vec3 Full";
@@ -189,12 +198,15 @@
         mTestNames[6] = "Grain";
         mTestNames[7] = "Fisheye Full";
         mTestNames[8] = "Fisheye Relaxed";
-        mTestNames[9] = "Vignette Full";
-        mTestNames[10] = "Vignette Relaxed";
-        mTestNames[11] = "Vignette Approximate Full";
-        mTestNames[12] = "Vignette Approximate Relaxed";
-        mTestNames[13] = "Group Test (emulated)";
-        mTestNames[14] = "Group Test (native)";
+        mTestNames[9] = "Fisheye Approximate Full";
+        mTestNames[10] = "Fisheye Approximate Relaxed";
+        mTestNames[11] = "Vignette Full";
+        mTestNames[12] = "Vignette Relaxed";
+        mTestNames[13] = "Vignette Approximate Full";
+        mTestNames[14] = "Vignette Approximate Relaxed";
+        mTestNames[15] = "Group Test (emulated)";
+        mTestNames[16] = "Group Test (native)";
+        mTestNames[17] = "Intrinsics Convolve 3x3";
         mTestSpinner.setAdapter(new ArrayAdapter<String>(
             this, R.layout.spinner_layout, mTestNames));
     }
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Intrinsics.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Intrinsics.java
new file mode 100644
index 0000000..ea8a018
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Intrinsics.java
@@ -0,0 +1,62 @@
+/*
+ * 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.rs.image;
+
+import java.lang.Math;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.Script;
+import android.renderscript.ScriptIntrinsicConvolve3x3;
+import android.renderscript.Type;
+import android.util.Log;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class Intrinsics extends TestBase {
+    private ScriptIntrinsicConvolve3x3 mScript;
+
+    Intrinsics(int id) {
+    }
+
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        t.setText("Strength");
+        b.setProgress(50);
+        return true;
+    }
+
+    public void onBar1Changed(int progress) {
+        float s = progress / 100.0f;
+        float v[] = new float[9];
+        v[0] = 0.f;     v[1] = -s;      v[2] = 0.f;
+        v[3] = -s;      v[4] = s*4+1;   v[5] = -s;
+        v[6] = 0.f;     v[7] = -s;      v[8] = 0.f;
+        mScript.setValues(v);
+    }
+
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = ScriptIntrinsicConvolve3x3.create(mRS, Element.RGBA_8888(mRS));
+    }
+
+    public void runTest() {
+        mScript.forEach(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye.rsh b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye.rsh
index 4dcfc1d..3809912 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye.rsh
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye.rsh
@@ -17,46 +17,41 @@
 rs_allocation in_alloc;
 rs_sampler sampler;
 
-static float2 center, dimensions;
-static float2 scale;
-static float alpha;
-static float radius2;
-static float factor;
+static float2 center, neg_center, inv_dimensions, axis_scale;
+static float alpha, radius2, factor;
 
-void init_filter(uint32_t dim_x, uint32_t dim_y, float focus_x, float focus_y, float k) {
-    center.x = focus_x;
-    center.y = focus_y;
-    dimensions.x = (float)dim_x;
-    dimensions.y = (float)dim_y;
-
+void init_filter(uint32_t dim_x, uint32_t dim_y, float center_x, float center_y, float k) {
+    center.x = center_x;
+    center.y = center_y;
+    neg_center = -center;
+    inv_dimensions.x = 1.f / (float)dim_x;
+    inv_dimensions.y = 1.f / (float)dim_y;
     alpha = k * 2.0 + 0.75;
-    float bound2 = 0.25;
-    if (dim_x > dim_y) {
-        scale.x = 1.0;
-        scale.y = dimensions.y / dimensions.x;
-        bound2 *= (scale.y*scale.y + 1);
-    } else {
-        scale.x = dimensions.x / dimensions.y;
-        scale.y = 1.0;
-        bound2 *= (scale.x*scale.x + 1);
-    }
+
+    axis_scale = (float2)1.f;
+    if (dim_x > dim_y)
+        axis_scale.y = (float)dim_y / (float)dim_x;
+    else
+        axis_scale.x = (float)dim_x / (float)dim_y;
+    
+    const float bound2 = 0.25 * (axis_scale.x*axis_scale.x + axis_scale.y*axis_scale.y);
     const float bound = sqrt(bound2);
     const float radius = 1.15 * bound;
     radius2 = radius*radius;
-    const float max_radian = 0.5f * M_PI - atan(alpha / bound * sqrt(radius2 - bound2));
+    const float max_radian = M_PI_2 - atan(alpha / bound * sqrt(radius2 - bound2));
     factor = bound / max_radian;
 }
 
 void root(uchar4 *out, uint32_t x, uint32_t y) {
     // Convert x and y to floating point coordinates with center as origin
-    float2 coord;
-    coord.x = (float)x / dimensions.x;
-    coord.y = (float)y / dimensions.y;
-    coord -= center;
-    const float dist = length(scale * coord);
-    const float radian = M_PI_2 - atan((alpha * sqrt(radius2 - dist * dist)) / dist);
-    const float scalar = radian * factor / dist;
-    const float2 new_coord = coord * scalar + center;
+    const float2 inCoord = {(float)x, (float)y};
+    const float2 coord = mad(inCoord, inv_dimensions, neg_center);
+    const float2 scaledCoord = axis_scale * coord;
+    const float dist2 = scaledCoord.x*scaledCoord.x + scaledCoord.y*scaledCoord.y;
+    const float inv_dist = rsqrt(dist2);
+    const float radian = M_PI_2 - atan((alpha * sqrt(radius2 - dist2)) * inv_dist);
+    const float scalar = radian * factor * inv_dist;
+    const float2 new_coord = mad(coord, scalar, center);
     const float4 fout = rsSample(in_alloc, sampler, new_coord);
     *out = rsPackColorTo8888(fout);
 }
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx.rsh b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx.rsh
new file mode 100644
index 0000000..008acbe
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx.rsh
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+rs_allocation in_alloc;
+rs_sampler sampler;
+
+static float2 center, neg_center, inv_dimensions, axis_scale;
+static float alpha, radius2, factor;
+
+void init_filter(uint32_t dim_x, uint32_t dim_y, float center_x, float center_y, float k) {
+    center.x = center_x;
+    center.y = center_y;
+    neg_center = -center;
+    inv_dimensions.x = 1.f / (float)dim_x;
+    inv_dimensions.y = 1.f / (float)dim_y;
+    alpha = k * 2.0 + 0.75;
+
+    axis_scale = (float2)1.f;
+    if (dim_x > dim_y)
+        axis_scale.y = (float)dim_y / (float)dim_x;
+    else
+        axis_scale.x = (float)dim_x / (float)dim_y;
+
+    const float bound2 = 0.25 * (axis_scale.x*axis_scale.x + axis_scale.y*axis_scale.y);
+    const float bound = sqrt(bound2);
+    const float radius = 1.15 * bound;
+    radius2 = radius*radius;
+    const float max_radian = M_PI_2 - atan(alpha / bound * sqrt(radius2 - bound2));
+    factor = bound / max_radian;
+}
+
+void root(uchar4 *out, uint32_t x, uint32_t y) {
+    // Convert x and y to floating point coordinates with center as origin
+    const float2 inCoord = {(float)x, (float)y};
+    const float2 coord = mad(inCoord, inv_dimensions, neg_center);
+    const float2 scaledCoord = axis_scale * coord;
+    const float dist2 = scaledCoord.x*scaledCoord.x + scaledCoord.y*scaledCoord.y;
+    const float inv_dist = approx_rsqrt(dist2);
+    const float radian = M_PI_2 - approx_atan((alpha * approx_sqrt(radius2 - dist2)) * inv_dist);
+    const float scalar = radian * factor * inv_dist;
+    const float2 new_coord = mad(coord, scalar, center);
+    const float4 fout = rsSample(in_alloc, sampler, new_coord);
+    *out = rsPackColorTo8888(fout);
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_full.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_full.rs
new file mode 100644
index 0000000..1ea37db
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_full.rs
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image)
+
+#include "fisheye_approx.rsh"
+
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_relaxed.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_relaxed.rs
new file mode 100644
index 0000000..3e76368
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/fisheye_approx_relaxed.rs
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image)
+#pragma rs_fp_relaxed
+
+#include "fisheye_approx.rsh"
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/Android.mk b/tests/RenderScriptTests/ImageProcessing2/Android.mk
new file mode 100644
index 0000000..c81fd93
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/Android.mk
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2009 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+                   $(call all-renderscript-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android.support.v8.renderscript
+
+LOCAL_PACKAGE_NAME := ImageProcessing2
+
+LOCAL_RENDERSCRIPT_FLAGS := -rs-package-name=android.support.v8.renderscript
+LOCAL_REQUIRED_MODULES := librsjni
+
+include $(BUILD_PACKAGE)
+
+#include $(call all-makefiles-under, $(LOCAL_PATH))
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/AndroidManifest.xml b/tests/RenderScriptTests/ImageProcessing2/AndroidManifest.xml
new file mode 100644
index 0000000..1ef04c2
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/AndroidManifest.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.rs.image2">
+    <uses-sdk android:minSdkVersion="11" />
+    <application android:label="IP GB"
+                 android:hardwareAccelerated="true">
+        <activity android:name="ImageProcessingActivity2">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/RenderScriptTests/ImageProcessing2/res/drawable-nodpi/city.png b/tests/RenderScriptTests/ImageProcessing2/res/drawable-nodpi/city.png
new file mode 100644
index 0000000..856eeff
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/res/drawable-nodpi/city.png
Binary files differ
diff --git a/tests/RenderScriptTests/ImageProcessing2/res/layout/main.xml b/tests/RenderScriptTests/ImageProcessing2/res/layout/main.xml
new file mode 100644
index 0000000..bd56d62
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/res/layout/main.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+            android:orientation="vertical"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:id="@+id/toplevel">
+    <SurfaceView
+        android:id="@+id/surface"
+        android:layout_width="1dip"
+        android:layout_height="1dip" />
+    <ScrollView
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent">
+            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:orientation="vertical"
+                android:layout_width="fill_parent"
+                android:layout_height="fill_parent">
+            <ImageView
+                android:id="@+id/display"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
+            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:orientation="horizontal"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content">
+                    <Button
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="@string/benchmark"
+                        android:onClick="benchmark"/>
+                    <TextView
+                        android:id="@+id/benchmarkText"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:textSize="8pt"
+                        android:text="@string/saturation"/>
+            </LinearLayout>
+            <Spinner
+                android:id="@+id/filterselection"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"/>
+            <TextView
+                android:id="@+id/slider1Text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textSize="8pt"
+                android:layout_marginLeft="10sp"
+                android:layout_marginTop="15sp"
+                android:text="@string/saturation"/>
+             <SeekBar
+                android:id="@+id/slider1"
+                android:layout_marginLeft="10sp"
+                android:layout_marginRight="10sp"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"/>
+            <TextView
+                android:id="@+id/slider2Text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textSize="8pt"
+                android:layout_marginLeft="10sp"
+                android:layout_marginTop="15sp"
+                android:text="@string/gamma"/>
+            <SeekBar
+                android:id="@+id/slider2"
+                android:layout_marginLeft="10sp"
+                android:layout_marginRight="10sp"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"/>
+            <TextView
+                android:id="@+id/slider3Text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="10sp"
+                android:layout_marginTop="15sp"
+                android:textSize="8pt"
+                android:text="@string/out_white"/>
+            <SeekBar
+                android:id="@+id/slider3"
+                android:layout_marginLeft="10sp"
+                android:layout_marginRight="10sp"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"/>
+            <TextView
+                android:id="@+id/slider4Text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textSize="8pt"
+                android:layout_marginLeft="10sp"
+                android:layout_marginTop="15sp"
+                android:text="@string/in_white"/>
+            <SeekBar
+                android:id="@+id/slider4"
+                android:layout_marginLeft="10sp"
+                android:layout_marginRight="10sp"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"/>
+            <TextView
+                android:id="@+id/slider5Text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textSize="8pt"
+                android:layout_marginLeft="10sp"
+                android:layout_marginTop="15sp"
+                android:text="@string/in_white"/>
+            <SeekBar
+                android:id="@+id/slider5"
+                android:layout_marginLeft="10sp"
+                android:layout_marginRight="10sp"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"/>
+            </LinearLayout>
+    </ScrollView>
+</LinearLayout>
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/res/layout/spinner_layout.xml b/tests/RenderScriptTests/ImageProcessing2/res/layout/spinner_layout.xml
new file mode 100644
index 0000000..8196bbf
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/res/layout/spinner_layout.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- 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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:padding="10dp"
+    android:textSize="16sp"
+/>
diff --git a/tests/RenderScriptTests/ImageProcessing2/res/values/strings.xml b/tests/RenderScriptTests/ImageProcessing2/res/values/strings.xml
new file mode 100644
index 0000000..cc5cc4d
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/res/values/strings.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* 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 License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- General -->
+    <skip />
+    <!--slider label -->
+    <string name="blur_description">Blur Radius</string>
+    <string name="in_white">In White</string>
+    <string name="out_white">Out White</string>
+    <string name="in_black">In Black</string>
+    <string name="out_black">Out Black</string>
+    <string name="gamma">Gamma</string>
+    <string name="saturation">Saturation</string>
+    <string name="benchmark">Benchmark</string>
+
+</resources>
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blur25.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blur25.java
new file mode 100644
index 0000000..be87716
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blur25.java
@@ -0,0 +1,101 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class Blur25 extends TestBase {
+    private int MAX_RADIUS = 25;
+    private ScriptC_threshold mScript;
+    private ScriptC_vertical_blur mScriptVBlur;
+    private ScriptC_horizontal_blur mScriptHBlur;
+    private int mRadius = MAX_RADIUS;
+    private float mSaturation = 1.0f;
+    private Allocation mScratchPixelsAllocation1;
+    private Allocation mScratchPixelsAllocation2;
+
+
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        t.setText("Radius");
+        b.setProgress(100);
+        return true;
+    }
+    public boolean onBar2Setup(SeekBar b, TextView t) {
+        b.setProgress(50);
+        t.setText("Saturation");
+        return true;
+    }
+
+
+    public void onBar1Changed(int progress) {
+        float fRadius = progress / 100.0f;
+        fRadius *= (float)(MAX_RADIUS);
+        mRadius = (int)fRadius;
+        mScript.set_radius(mRadius);
+    }
+    public void onBar2Changed(int progress) {
+        mSaturation = (float)progress / 50.0f;
+        mScriptVBlur.invoke_setSaturation(mSaturation);
+    }
+
+
+    public void createTest(android.content.res.Resources res) {
+        int width = mInPixelsAllocation.getType().getX();
+        int height = mInPixelsAllocation.getType().getY();
+
+        Type.Builder tb = new Type.Builder(mRS, Element.F32_4(mRS));
+        tb.setX(width);
+        tb.setY(height);
+        mScratchPixelsAllocation1 = Allocation.createTyped(mRS, tb.create());
+        mScratchPixelsAllocation2 = Allocation.createTyped(mRS, tb.create());
+
+        mScriptVBlur = new ScriptC_vertical_blur(mRS, res, R.raw.vertical_blur);
+        mScriptHBlur = new ScriptC_horizontal_blur(mRS, res, R.raw.horizontal_blur);
+
+        mScript = new ScriptC_threshold(mRS, res, R.raw.threshold);
+        mScript.set_width(width);
+        mScript.set_height(height);
+        mScript.set_radius(mRadius);
+
+        mScriptVBlur.invoke_setSaturation(mSaturation);
+
+        mScript.bind_InPixel(mInPixelsAllocation);
+        mScript.bind_OutPixel(mOutPixelsAllocation);
+        mScript.bind_ScratchPixel1(mScratchPixelsAllocation1);
+        mScript.bind_ScratchPixel2(mScratchPixelsAllocation2);
+
+        mScript.set_vBlurScript(mScriptVBlur);
+        mScript.set_hBlurScript(mScriptHBlur);
+    }
+
+    public void runTest() {
+        mScript.invoke_filter();
+    }
+
+    public void setupBenchmark() {
+        mScript.set_radius(MAX_RADIUS);
+    }
+
+    public void exitBenchmark() {
+        mScript.set_radius(mRadius);
+    }
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Fisheye.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Fisheye.java
new file mode 100644
index 0000000..995cf9d
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Fisheye.java
@@ -0,0 +1,103 @@
+/*
+ * 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.rs.image2;
+
+import android.support.v8.renderscript.*;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class Fisheye extends TestBase {
+    private ScriptC_fisheye_full mScript_full = null;
+    private ScriptC_fisheye_relaxed mScript_relaxed = null;
+    private final boolean relaxed;
+    private float center_x = 0.5f;
+    private float center_y = 0.5f;
+    private float scale = 0.5f;
+
+    public Fisheye(boolean relaxed) {
+        this.relaxed = relaxed;
+    }
+
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        t.setText("Scale");
+        b.setMax(100);
+        b.setProgress(25);
+        return true;
+    }
+    public boolean onBar2Setup(SeekBar b, TextView t) {
+        t.setText("Shift center X");
+        b.setMax(100);
+        b.setProgress(50);
+        return true;
+    }
+    public boolean onBar3Setup(SeekBar b, TextView t) {
+        t.setText("Shift center Y");
+        b.setMax(100);
+        b.setProgress(50);
+        return true;
+    }
+
+    public void onBar1Changed(int progress) {
+        scale = progress / 50.0f;
+        do_init();
+    }
+    public void onBar2Changed(int progress) {
+        center_x = progress / 100.0f;
+        do_init();
+    }
+    public void onBar3Changed(int progress) {
+        center_y = progress / 100.0f;
+        do_init();
+    }
+
+    private void do_init() {
+        if (relaxed)
+            mScript_relaxed.invoke_init_filter(
+                    mInPixelsAllocation.getType().getX(),
+                    mInPixelsAllocation.getType().getY(), center_x, center_y,
+                    scale);
+        else
+            mScript_full.invoke_init_filter(
+                    mInPixelsAllocation.getType().getX(),
+                    mInPixelsAllocation.getType().getY(), center_x, center_y,
+                    scale);
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        if (relaxed) {
+            mScript_relaxed = new ScriptC_fisheye_relaxed(mRS, res,
+                    R.raw.fisheye_relaxed);
+            mScript_relaxed.set_in_alloc(mInPixelsAllocation);
+            mScript_relaxed.set_sampler(Sampler.CLAMP_LINEAR(mRS));
+        } else {
+            mScript_full = new ScriptC_fisheye_full(mRS, res,
+                    R.raw.fisheye_full);
+            mScript_full.set_in_alloc(mInPixelsAllocation);
+            mScript_full.set_sampler(Sampler.CLAMP_LINEAR(mRS));
+        }
+        do_init();
+    }
+
+    public void runTest() {
+        if (relaxed)
+            mScript_relaxed.forEach_root(mOutPixelsAllocation);
+        else
+            mScript_full.forEach_root(mOutPixelsAllocation);
+    }
+
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Grain.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Grain.java
new file mode 100644
index 0000000..e00edd7
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Grain.java
@@ -0,0 +1,68 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class Grain extends TestBase {
+    private ScriptC_grain mScript;
+    private Allocation mNoise;
+    private Allocation mNoise2;
+
+
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        t.setText("Strength");
+        b.setProgress(50);
+        return true;
+    }
+
+    public void onBar1Changed(int progress) {
+        float s = progress / 100.0f;
+        mScript.set_gNoiseStrength(s);
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        int width = mInPixelsAllocation.getType().getX();
+        int height = mInPixelsAllocation.getType().getY();
+
+        Type.Builder tb = new Type.Builder(mRS, Element.U8(mRS));
+        tb.setX(width);
+        tb.setY(height);
+        mNoise = Allocation.createTyped(mRS, tb.create());
+        mNoise2 = Allocation.createTyped(mRS, tb.create());
+
+        mScript = new ScriptC_grain(mRS, res, R.raw.grain);
+        mScript.set_gWidth(width);
+        mScript.set_gHeight(height);
+        mScript.set_gNoiseStrength(0.5f);
+        mScript.set_gBlendSource(mNoise);
+        mScript.set_gNoise(mNoise2);
+    }
+
+    public void runTest() {
+        mScript.forEach_genRand(mNoise);
+        mScript.forEach_blend9(mNoise2);
+        mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Greyscale.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Greyscale.java
new file mode 100644
index 0000000..2d85ae7
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Greyscale.java
@@ -0,0 +1,35 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+
+public class Greyscale extends TestBase {
+    private ScriptC_greyscale mScript;
+
+    public void createTest(android.content.res.Resources res) {
+        mScript = new ScriptC_greyscale(mRS, res, R.raw.greyscale);
+    }
+
+    public void runTest() {
+        mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/GroupTest.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/GroupTest.java
new file mode 100644
index 0000000..b9fbb59
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/GroupTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+
+public class GroupTest extends TestBase {
+    private ScriptC_convolve3x3 mConvolve;
+    private ScriptC_colormatrix mMatrix;
+
+    private Allocation mScratchPixelsAllocation1;
+    private ScriptGroup mGroup;
+
+    private int mWidth;
+    private int mHeight;
+    private boolean mUseNative;
+
+
+    public GroupTest(boolean useNative) {
+        mUseNative = useNative;
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        mWidth = mInPixelsAllocation.getType().getX();
+        mHeight = mInPixelsAllocation.getType().getY();
+
+        mConvolve = new ScriptC_convolve3x3(mRS, res, R.raw.convolve3x3);
+        mMatrix = new ScriptC_colormatrix(mRS, res, R.raw.colormatrix);
+
+        float f[] = new float[9];
+        f[0] =  0.f;    f[1] = -1.f;    f[2] =  0.f;
+        f[3] = -1.f;    f[4] =  5.f;    f[5] = -1.f;
+        f[6] =  0.f;    f[7] = -1.f;    f[8] =  0.f;
+        mConvolve.set_gCoeffs(f);
+
+        Matrix4f m = new Matrix4f();
+        m.set(1, 0, 0.2f);
+        m.set(1, 1, 0.9f);
+        m.set(1, 2, 0.2f);
+        mMatrix.invoke_setMatrix(m);
+
+        Type.Builder tb = new Type.Builder(mRS, Element.U8_4(mRS));
+        tb.setX(mWidth);
+        tb.setY(mHeight);
+        Type connect = tb.create();
+
+        if (mUseNative) {
+            ScriptGroup.Builder b = new ScriptGroup.Builder(mRS);
+            b.addConnection(connect, mConvolve, mMatrix, null);
+            mGroup = b.create();
+
+        } else {
+            mScratchPixelsAllocation1 = Allocation.createTyped(mRS, connect);
+        }
+    }
+
+    public void runTest() {
+        mConvolve.set_gIn(mInPixelsAllocation);
+        mConvolve.set_gWidth(mWidth);
+        mConvolve.set_gHeight(mHeight);
+        if (mUseNative) {
+            mGroup.setOutput(mMatrix, mOutPixelsAllocation);
+            mGroup.execute();
+        } else {
+            mConvolve.forEach_root(mScratchPixelsAllocation1);
+            mMatrix.forEach_root(mScratchPixelsAllocation1, mOutPixelsAllocation);
+        }
+    }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ImageProcessingActivity2.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ImageProcessingActivity2.java
new file mode 100644
index 0000000..9b36da14
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ImageProcessingActivity2.java
@@ -0,0 +1,291 @@
+/*
+ * 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.rs.image2;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.graphics.BitmapFactory;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.support.v8.renderscript.*;
+import android.view.SurfaceView;
+import android.view.SurfaceHolder;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.view.View;
+import android.util.Log;
+import java.lang.Math;
+
+public class ImageProcessingActivity2 extends Activity
+                                       implements SeekBar.OnSeekBarChangeListener {
+    private final String TAG = "Img";
+    Bitmap mBitmapIn;
+    Bitmap mBitmapOut;
+    String mTestNames[];
+
+    private SeekBar mBar1;
+    private SeekBar mBar2;
+    private SeekBar mBar3;
+    private SeekBar mBar4;
+    private SeekBar mBar5;
+    private TextView mText1;
+    private TextView mText2;
+    private TextView mText3;
+    private TextView mText4;
+    private TextView mText5;
+
+    private float mSaturation = 1.0f;
+
+    private TextView mBenchmarkResult;
+    private Spinner mTestSpinner;
+
+    private SurfaceView mSurfaceView;
+    private ImageView mDisplayView;
+
+    private boolean mDoingBenchmark;
+
+    private TestBase mTest;
+
+
+    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+        if (fromUser) {
+
+            if (seekBar == mBar1) {
+                mTest.onBar1Changed(progress);
+            } else if (seekBar == mBar2) {
+                mTest.onBar2Changed(progress);
+            } else if (seekBar == mBar3) {
+                mTest.onBar3Changed(progress);
+            } else if (seekBar == mBar4) {
+                mTest.onBar4Changed(progress);
+            } else if (seekBar == mBar5) {
+                mTest.onBar5Changed(progress);
+            }
+
+            mTest.runTest();
+            mTest.updateBitmap(mBitmapOut);
+            mDisplayView.invalidate();
+        }
+    }
+
+    public void onStartTrackingTouch(SeekBar seekBar) {
+    }
+
+    public void onStopTrackingTouch(SeekBar seekBar) {
+    }
+
+    void setupBars() {
+        mBar1.setVisibility(View.VISIBLE);
+        mText1.setVisibility(View.VISIBLE);
+        mTest.onBar1Setup(mBar1, mText1);
+
+        mBar2.setVisibility(View.VISIBLE);
+        mText2.setVisibility(View.VISIBLE);
+        mTest.onBar2Setup(mBar2, mText2);
+
+        mBar3.setVisibility(View.VISIBLE);
+        mText3.setVisibility(View.VISIBLE);
+        mTest.onBar3Setup(mBar3, mText3);
+
+        mBar4.setVisibility(View.VISIBLE);
+        mText4.setVisibility(View.VISIBLE);
+        mTest.onBar4Setup(mBar4, mText4);
+
+        mBar5.setVisibility(View.VISIBLE);
+        mText5.setVisibility(View.VISIBLE);
+        mTest.onBar5Setup(mBar5, mText5);
+    }
+
+
+    void changeTest(int testID) {
+        switch(testID) {
+        case 0:
+            mTest = new LevelsV4(false, false);
+            break;
+        case 1:
+            mTest = new LevelsV4(false, true);
+            break;
+        case 2:
+            mTest = new LevelsV4(true, false);
+            break;
+        case 3:
+            mTest = new LevelsV4(true, true);
+            break;
+        case 4:
+            mTest = new Blur25();
+            break;
+        case 5:
+            mTest = new Greyscale();
+            break;
+        case 6:
+            mTest = new Grain();
+            break;
+        case 7:
+            mTest = new Fisheye(false);
+            break;
+        case 8:
+            mTest = new Fisheye(true);
+            break;
+        case 9:
+            mTest = new Vignette(false);
+            break;
+        case 10:
+            mTest = new Vignette(true);
+            break;
+        case 11:
+            mTest = new GroupTest(false);
+            break;
+        case 12:
+            mTest = new GroupTest(true);
+            break;
+        }
+
+        mTest.createBaseTest(this, mBitmapIn);
+        setupBars();
+
+        mTest.runTest();
+        mTest.updateBitmap(mBitmapOut);
+        mDisplayView.invalidate();
+        mBenchmarkResult.setText("Result: not run");
+    }
+
+    void setupTests() {
+        mTestNames = new String[13];
+        mTestNames[0] = "Levels Vec3 Relaxed";
+        mTestNames[1] = "Levels Vec4 Relaxed";
+        mTestNames[2] = "Levels Vec3 Full";
+        mTestNames[3] = "Levels Vec4 Full";
+        mTestNames[4] = "Blur radius 25";
+        mTestNames[5] = "Greyscale";
+        mTestNames[6] = "Grain";
+        mTestNames[7] = "Fisheye Full";
+        mTestNames[8] = "Fisheye Relaxed";
+        mTestNames[9] = "Vignette Full";
+        mTestNames[10] = "Vignette Relaxed";
+        mTestNames[11] = "Group Test (emulated)";
+        mTestNames[12] = "Group Test (native)";
+        mTestSpinner.setAdapter(new ArrayAdapter<String>(
+            this, R.layout.spinner_layout, mTestNames));
+    }
+
+    private AdapterView.OnItemSelectedListener mTestSpinnerListener =
+            new AdapterView.OnItemSelectedListener() {
+                public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+                    changeTest(pos);
+                }
+
+                public void onNothingSelected(AdapterView parent) {
+
+                }
+            };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        mBitmapIn = loadBitmap(R.drawable.city);
+        mBitmapOut = loadBitmap(R.drawable.city);
+
+        mSurfaceView = (SurfaceView) findViewById(R.id.surface);
+
+        mDisplayView = (ImageView) findViewById(R.id.display);
+        mDisplayView.setImageBitmap(mBitmapOut);
+
+        mBar1 = (SeekBar) findViewById(R.id.slider1);
+        mBar2 = (SeekBar) findViewById(R.id.slider2);
+        mBar3 = (SeekBar) findViewById(R.id.slider3);
+        mBar4 = (SeekBar) findViewById(R.id.slider4);
+        mBar5 = (SeekBar) findViewById(R.id.slider5);
+
+        mBar1.setOnSeekBarChangeListener(this);
+        mBar2.setOnSeekBarChangeListener(this);
+        mBar3.setOnSeekBarChangeListener(this);
+        mBar4.setOnSeekBarChangeListener(this);
+        mBar5.setOnSeekBarChangeListener(this);
+
+        mText1 = (TextView) findViewById(R.id.slider1Text);
+        mText2 = (TextView) findViewById(R.id.slider2Text);
+        mText3 = (TextView) findViewById(R.id.slider3Text);
+        mText4 = (TextView) findViewById(R.id.slider4Text);
+        mText5 = (TextView) findViewById(R.id.slider5Text);
+
+        mTestSpinner = (Spinner) findViewById(R.id.filterselection);
+        mTestSpinner.setOnItemSelectedListener(mTestSpinnerListener);
+
+        mBenchmarkResult = (TextView) findViewById(R.id.benchmarkText);
+        mBenchmarkResult.setText("Result: not run");
+
+        setupTests();
+        changeTest(0);
+    }
+
+
+    private Bitmap loadBitmap(int resource) {
+        final BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
+        return copyBitmap(BitmapFactory.decodeResource(getResources(), resource, options));
+    }
+
+    private static Bitmap copyBitmap(Bitmap source) {
+        Bitmap b = Bitmap.createBitmap(source.getWidth(), source.getHeight(), source.getConfig());
+        Canvas c = new Canvas(b);
+        c.drawBitmap(source, 0, 0, null);
+        source.recycle();
+        return b;
+    }
+
+    // button hook
+    public void benchmark(View v) {
+        long t = getBenchmark();
+        //long javaTime = javaFilter();
+        //mBenchmarkResult.setText("RS: " + t + " ms  Java: " + javaTime + " ms");
+        mBenchmarkResult.setText("Result: " + t + " ms");
+    }
+
+    // For benchmark test
+    public long getBenchmark() {
+        mDoingBenchmark = true;
+
+        mTest.setupBenchmark();
+        long result = 0;
+
+        Log.v(TAG, "Warming");
+        long t = java.lang.System.currentTimeMillis() + 2000;
+        do {
+            mTest.runTest();
+            mTest.finish();
+        } while (t > java.lang.System.currentTimeMillis());
+
+
+        Log.v(TAG, "Benchmarking");
+        t = java.lang.System.currentTimeMillis();
+        mTest.runTest();
+        mTest.finish();
+        t = java.lang.System.currentTimeMillis() - t;
+
+        Log.v(TAG, "getBenchmark: Renderscript frame time core ms " + t);
+        mTest.exitBenchmark();
+        mDoingBenchmark = false;
+
+        return t;
+    }
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/LevelsV4.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/LevelsV4.java
new file mode 100644
index 0000000..fbe3727
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/LevelsV4.java
@@ -0,0 +1,161 @@
+/*
+ * 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.rs.image2;
+
+import java.lang.Math;
+
+import android.support.v8.renderscript.*;
+import android.util.Log;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+
+public class LevelsV4 extends TestBase {
+    private ScriptC_levels_relaxed mScriptR;
+    private ScriptC_levels_full mScriptF;
+    private float mInBlack = 0.0f;
+    private float mOutBlack = 0.0f;
+    private float mInWhite = 255.0f;
+    private float mOutWhite = 255.0f;
+    private float mSaturation = 1.0f;
+
+    Matrix3f satMatrix = new Matrix3f();
+    float mInWMinInB;
+    float mOutWMinOutB;
+    float mOverInWMinInB;
+
+    boolean mUseFull;
+    boolean mUseV4;
+
+    LevelsV4(boolean useFull, boolean useV4) {
+        mUseFull = useFull;
+        mUseV4 = useV4;
+    }
+
+
+    private void setLevels() {
+        mInWMinInB = mInWhite - mInBlack;
+        mOutWMinOutB = mOutWhite - mOutBlack;
+        mOverInWMinInB = 1.f / mInWMinInB;
+
+        mScriptR.set_inBlack(mInBlack);
+        mScriptR.set_outBlack(mOutBlack);
+        mScriptR.set_inWMinInB(mInWMinInB);
+        mScriptR.set_outWMinOutB(mOutWMinOutB);
+        mScriptR.set_overInWMinInB(mOverInWMinInB);
+        mScriptF.set_inBlack(mInBlack);
+        mScriptF.set_outBlack(mOutBlack);
+        mScriptF.set_inWMinInB(mInWMinInB);
+        mScriptF.set_outWMinOutB(mOutWMinOutB);
+        mScriptF.set_overInWMinInB(mOverInWMinInB);
+    }
+
+    private void setSaturation() {
+        float rWeight = 0.299f;
+        float gWeight = 0.587f;
+        float bWeight = 0.114f;
+        float oneMinusS = 1.0f - mSaturation;
+
+        satMatrix.set(0, 0, oneMinusS * rWeight + mSaturation);
+        satMatrix.set(0, 1, oneMinusS * rWeight);
+        satMatrix.set(0, 2, oneMinusS * rWeight);
+        satMatrix.set(1, 0, oneMinusS * gWeight);
+        satMatrix.set(1, 1, oneMinusS * gWeight + mSaturation);
+        satMatrix.set(1, 2, oneMinusS * gWeight);
+        satMatrix.set(2, 0, oneMinusS * bWeight);
+        satMatrix.set(2, 1, oneMinusS * bWeight);
+        satMatrix.set(2, 2, oneMinusS * bWeight + mSaturation);
+        mScriptR.set_colorMat(satMatrix);
+        mScriptF.set_colorMat(satMatrix);
+    }
+
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        b.setProgress(50);
+        t.setText("Saturation");
+        return true;
+    }
+    public boolean onBar2Setup(SeekBar b, TextView t) {
+        b.setMax(128);
+        b.setProgress(0);
+        t.setText("In Black");
+        return true;
+    }
+    public boolean onBar3Setup(SeekBar b, TextView t) {
+        b.setMax(128);
+        b.setProgress(0);
+        t.setText("Out Black");
+        return true;
+    }
+    public boolean onBar4Setup(SeekBar b, TextView t) {
+        b.setMax(128);
+        b.setProgress(128);
+        t.setText("Out White");
+        return true;
+    }
+    public boolean onBar5Setup(SeekBar b, TextView t) {
+        b.setMax(128);
+        b.setProgress(128);
+        t.setText("Out White");
+        return true;
+    }
+
+    public void onBar1Changed(int progress) {
+        mSaturation = (float)progress / 50.0f;
+        setSaturation();
+    }
+    public void onBar2Changed(int progress) {
+        mInBlack = (float)progress;
+        setLevels();
+    }
+    public void onBar3Changed(int progress) {
+        mOutBlack = (float)progress;
+        setLevels();
+    }
+    public void onBar4Changed(int progress) {
+        mInWhite = (float)progress + 127.0f;
+        setLevels();
+    }
+    public void onBar5Changed(int progress) {
+        mOutWhite = (float)progress + 127.0f;
+        setLevels();
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        mScriptR = new ScriptC_levels_relaxed(mRS, res, R.raw.levels_relaxed);
+        mScriptF = new ScriptC_levels_full(mRS, res, R.raw.levels_full);
+        setSaturation();
+        setLevels();
+    }
+
+    public void runTest() {
+        if (mUseFull) {
+            if (mUseV4) {
+                mScriptF.forEach_root4(mInPixelsAllocation, mOutPixelsAllocation);
+            } else {
+                mScriptF.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+            }
+        } else {
+            if (mUseV4) {
+                mScriptR.forEach_root4(mInPixelsAllocation, mOutPixelsAllocation);
+            } else {
+                mScriptR.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+            }
+        }
+    }
+
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/TestBase.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/TestBase.java
new file mode 100644
index 0000000..35170af
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/TestBase.java
@@ -0,0 +1,117 @@
+/*
+ * 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.rs.image2;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.graphics.BitmapFactory;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.support.v8.renderscript.*;
+import android.view.SurfaceView;
+import android.view.SurfaceHolder;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.view.View;
+import android.util.Log;
+import java.lang.Math;
+
+public class TestBase  {
+    protected final String TAG = "Img";
+
+    protected RenderScript mRS;
+    protected Allocation mInPixelsAllocation;
+    protected Allocation mOutPixelsAllocation;
+
+    // Override to use UI elements
+    public void onBar1Changed(int progress) {
+    }
+    public void onBar2Changed(int progress) {
+    }
+    public void onBar3Changed(int progress) {
+    }
+    public void onBar4Changed(int progress) {
+    }
+    public void onBar5Changed(int progress) {
+    }
+
+    // Override to use UI elements
+    // Unused bars will be hidden.
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        b.setVisibility(View.INVISIBLE);
+        t.setVisibility(View.INVISIBLE);
+        return false;
+    }
+    public boolean onBar2Setup(SeekBar b, TextView t) {
+        b.setVisibility(View.INVISIBLE);
+        t.setVisibility(View.INVISIBLE);
+        return false;
+    }
+    public boolean onBar3Setup(SeekBar b, TextView t) {
+        b.setVisibility(View.INVISIBLE);
+        t.setVisibility(View.INVISIBLE);
+        return false;
+    }
+    public boolean onBar4Setup(SeekBar b, TextView t) {
+        b.setVisibility(View.INVISIBLE);
+        t.setVisibility(View.INVISIBLE);
+        return false;
+    }
+    public boolean onBar5Setup(SeekBar b, TextView t) {
+        b.setVisibility(View.INVISIBLE);
+        t.setVisibility(View.INVISIBLE);
+        return false;
+    }
+
+    public final void createBaseTest(ImageProcessingActivity2 act, Bitmap b) {
+        mRS = RenderScript.create(act);
+        mInPixelsAllocation = Allocation.createFromBitmap(mRS, b,
+                                                          Allocation.MipmapControl.MIPMAP_NONE,
+                                                          Allocation.USAGE_SCRIPT);
+        mOutPixelsAllocation = Allocation.createFromBitmap(mRS, b,
+                                                           Allocation.MipmapControl.MIPMAP_NONE,
+                                                           Allocation.USAGE_SCRIPT);
+        createTest(act.getResources());
+    }
+
+    // Must override
+    public void createTest(android.content.res.Resources res) {
+        android.util.Log.e("img", "implement createTest");
+    }
+
+    // Must override
+    public void runTest() {
+    }
+
+    public void finish() {
+        mRS.finish();
+    }
+
+    public void updateBitmap(Bitmap b) {
+        mOutPixelsAllocation.copyTo(b);
+    }
+
+    // Override to configure specific benchmark config.
+    public void setupBenchmark() {
+    }
+
+    // Override to reset after benchmark.
+    public void exitBenchmark() {
+    }
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Vignette.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Vignette.java
new file mode 100644
index 0000000..fc69eba
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Vignette.java
@@ -0,0 +1,121 @@
+/*
+ * 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.rs.image2;
+
+import android.support.v8.renderscript.*;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class Vignette extends TestBase {
+    private ScriptC_vignette_full mScript_full = null;
+    private ScriptC_vignette_relaxed mScript_relaxed = null;
+    private final boolean relaxed;
+    private float center_x = 0.5f;
+    private float center_y = 0.5f;
+    private float scale = 0.5f;
+    private float shade = 0.5f;
+    private float slope = 20.0f;
+
+    public Vignette(boolean relaxed) {
+        this.relaxed = relaxed;
+    }
+
+    public boolean onBar1Setup(SeekBar b, TextView t) {
+        t.setText("Scale");
+        b.setMax(100);
+        b.setProgress(25);
+        return true;
+    }
+    public boolean onBar2Setup(SeekBar b, TextView t) {
+        t.setText("Shade");
+        b.setMax(100);
+        b.setProgress(50);
+        return true;
+    }
+    public boolean onBar3Setup(SeekBar b, TextView t) {
+        t.setText("Slope");
+        b.setMax(100);
+        b.setProgress(20);
+        return true;
+    }
+    public boolean onBar4Setup(SeekBar b, TextView t) {
+        t.setText("Shift center X");
+        b.setMax(100);
+        b.setProgress(50);
+        return true;
+    }
+    public boolean onBar5Setup(SeekBar b, TextView t) {
+        t.setText("Shift center Y");
+        b.setMax(100);
+        b.setProgress(50);
+        return true;
+    }
+
+    public void onBar1Changed(int progress) {
+        scale = progress / 50.0f;
+        do_init();
+    }
+    public void onBar2Changed(int progress) {
+        shade = progress / 100.0f;
+        do_init();
+    }
+    public void onBar3Changed(int progress) {
+        slope = (float)progress;
+        do_init();
+    }
+    public void onBar4Changed(int progress) {
+        center_x = progress / 100.0f;
+        do_init();
+    }
+    public void onBar5Changed(int progress) {
+        center_y = progress / 100.0f;
+        do_init();
+    }
+
+    private void do_init() {
+        if (relaxed)
+            mScript_relaxed.invoke_init_vignette(
+                    mInPixelsAllocation.getType().getX(),
+                    mInPixelsAllocation.getType().getY(), center_x, center_y,
+                    scale, shade, slope);
+        else
+            mScript_full.invoke_init_vignette(
+                    mInPixelsAllocation.getType().getX(),
+                    mInPixelsAllocation.getType().getY(), center_x, center_y,
+                    scale, shade, slope);
+    }
+
+    public void createTest(android.content.res.Resources res) {
+        if (relaxed) {
+            mScript_relaxed = new ScriptC_vignette_relaxed(mRS, res,
+                    R.raw.vignette_relaxed);
+        } else {
+            mScript_full = new ScriptC_vignette_full(mRS, res,
+                    R.raw.vignette_full);
+        }
+        do_init();
+    }
+
+    public void runTest() {
+        if (relaxed)
+            mScript_relaxed.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+        else
+            mScript_full.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+    }
+
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.rs
new file mode 100644
index 0000000..e93bef3
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/colormatrix.rs
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+#pragma rs_fp_relaxed
+
+
+static rs_matrix4x4 Mat;
+
+void init() {
+    rsMatrixLoadIdentity(&Mat);
+}
+
+void setMatrix(rs_matrix4x4 m) {
+    Mat = m;
+}
+
+void root(const uchar4 *in, uchar4 *out) {
+    float4 f = convert_float4(*in);
+    f = rsMatrixMultiply(&Mat, f);
+    f = clamp(f, 0.f, 255.f);
+    *out = convert_uchar4(f);
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve3x3.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve3x3.rs
new file mode 100644
index 0000000..b55190c
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve3x3.rs
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+#pragma rs_fp_relaxed
+
+int32_t gWidth;
+int32_t gHeight;
+rs_allocation gIn;
+
+float gCoeffs[9];
+
+void root(uchar4 *out, uint32_t x, uint32_t y) {
+    uint32_t x1 = min((int32_t)x+1, gWidth);
+    uint32_t x2 = max((int32_t)x-1, 0);
+    uint32_t y1 = min((int32_t)y+1, gHeight);
+    uint32_t y2 = max((int32_t)y-1, 0);
+
+    float4 p00 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y1))[0]);
+    float4 p01 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x, y1))[0]);
+    float4 p02 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x2, y1))[0]);
+    float4 p10 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y))[0]);
+    float4 p11 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x, y))[0]);
+    float4 p12 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x2, y))[0]);
+    float4 p20 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y2))[0]);
+    float4 p21 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x, y2))[0]);
+    float4 p22 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x2, y2))[0]);
+    p00 *= gCoeffs[0];
+    p01 *= gCoeffs[1];
+    p02 *= gCoeffs[2];
+    p10 *= gCoeffs[3];
+    p11 *= gCoeffs[4];
+    p12 *= gCoeffs[5];
+    p20 *= gCoeffs[6];
+    p21 *= gCoeffs[7];
+    p22 *= gCoeffs[8];
+
+    p00 += p01;
+    p02 += p10;
+    p11 += p12;
+    p20 += p21;
+
+    p22 += p00;
+    p02 += p11;
+
+    p20 += p22;
+    p20 += p02;
+
+    p20 = clamp(p20, 0.f, 255.f);
+    *out = convert_uchar4(p20);
+}
+
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye.rsh b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye.rsh
new file mode 100644
index 0000000..4dcfc1d
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye.rsh
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+rs_allocation in_alloc;
+rs_sampler sampler;
+
+static float2 center, dimensions;
+static float2 scale;
+static float alpha;
+static float radius2;
+static float factor;
+
+void init_filter(uint32_t dim_x, uint32_t dim_y, float focus_x, float focus_y, float k) {
+    center.x = focus_x;
+    center.y = focus_y;
+    dimensions.x = (float)dim_x;
+    dimensions.y = (float)dim_y;
+
+    alpha = k * 2.0 + 0.75;
+    float bound2 = 0.25;
+    if (dim_x > dim_y) {
+        scale.x = 1.0;
+        scale.y = dimensions.y / dimensions.x;
+        bound2 *= (scale.y*scale.y + 1);
+    } else {
+        scale.x = dimensions.x / dimensions.y;
+        scale.y = 1.0;
+        bound2 *= (scale.x*scale.x + 1);
+    }
+    const float bound = sqrt(bound2);
+    const float radius = 1.15 * bound;
+    radius2 = radius*radius;
+    const float max_radian = 0.5f * M_PI - atan(alpha / bound * sqrt(radius2 - bound2));
+    factor = bound / max_radian;
+}
+
+void root(uchar4 *out, uint32_t x, uint32_t y) {
+    // Convert x and y to floating point coordinates with center as origin
+    float2 coord;
+    coord.x = (float)x / dimensions.x;
+    coord.y = (float)y / dimensions.y;
+    coord -= center;
+    const float dist = length(scale * coord);
+    const float radian = M_PI_2 - atan((alpha * sqrt(radius2 - dist * dist)) / dist);
+    const float scalar = radian * factor / dist;
+    const float2 new_coord = coord * scalar + center;
+    const float4 fout = rsSample(in_alloc, sampler, new_coord);
+    *out = rsPackColorTo8888(fout);
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_full.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_full.rs
new file mode 100644
index 0000000..e42df13
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_full.rs
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+
+#include "fisheye.rsh"
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_relaxed.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_relaxed.rs
new file mode 100644
index 0000000..990310b
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_relaxed.rs
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+#pragma rs_fp_relaxed
+
+#include "fisheye.rsh"
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.rs
new file mode 100644
index 0000000..75f4021
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.rs
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+#pragma rs_fp_relaxed
+
+void genRand(uchar *out) {
+    *out = (uchar)rsRand(0xff);
+}
+
+/*
+ * Convolution matrix of distance 2 with fixed point of 'kShiftBits' bits
+ * shifted. Thus the sum of this matrix should be 'kShiftValue'. Entries of
+ * small values are not calculated to gain efficiency.
+ * The order ot pixels represented in this matrix is:
+ *  1  2  3
+ *  4  0  5
+ *  6  7  8
+ *  and the matrix should be: {230, 56, 114, 56, 114, 114, 56, 114, 56}.
+ *  However, since most of the valus are identical, we only use the first three
+ *  entries and the entries corresponding to the pixels is:
+ *  1  2  1
+ *  2  0  2
+ *  1  2  1
+ */
+
+int32_t gWidth;
+int32_t gHeight;
+
+rs_allocation gBlendSource;
+void blend9(uchar *out, uint32_t x, uint32_t y) {
+    uint32_t x1 = min(x+1, (uint32_t)gWidth);
+    uint32_t x2 = max(x-1, (uint32_t)0);
+    uint32_t y1 = min(y+1, (uint32_t)gHeight);
+    uint32_t y2 = max(y-1, (uint32_t)0);
+
+    uint p00 = 56 *  ((uchar *)rsGetElementAt(gBlendSource, x1, y1))[0];
+    uint p01 = 114 * ((uchar *)rsGetElementAt(gBlendSource, x, y1))[0];
+    uint p02 = 56 *  ((uchar *)rsGetElementAt(gBlendSource, x2, y1))[0];
+    uint p10 = 114 * ((uchar *)rsGetElementAt(gBlendSource, x1, y))[0];
+    uint p11 = 230 * ((uchar *)rsGetElementAt(gBlendSource, x, y))[0];
+    uint p12 = 114 * ((uchar *)rsGetElementAt(gBlendSource, x2, y))[0];
+    uint p20 = 56 *  ((uchar *)rsGetElementAt(gBlendSource, x1, y2))[0];
+    uint p21 = 114 * ((uchar *)rsGetElementAt(gBlendSource, x, y2))[0];
+    uint p22 = 56 *  ((uchar *)rsGetElementAt(gBlendSource, x2, y2))[0];
+
+    p00 += p01;
+    p02 += p10;
+    p11 += p12;
+    p20 += p21;
+
+    p22 += p00;
+    p02 += p11;
+
+    p20 += p22;
+    p20 += p02;
+
+    *out = (uchar)(p20 >> 10);
+}
+
+float gNoiseStrength;
+
+rs_allocation gNoise;
+void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
+    float4 ip = convert_float4(*in);
+    float pnoise = (float) ((uchar *)rsGetElementAt(gNoise, x, y))[0];
+
+    float energy_level = ip.r + ip.g + ip.b;
+    float energy_mask = (28.f - sqrt(energy_level)) * 0.03571f;
+    pnoise = (pnoise - 128.f) * energy_mask;
+
+    ip += pnoise * gNoiseStrength;
+    ip = clamp(ip, 0.f, 255.f);
+
+    uchar4 p = convert_uchar4(ip);
+    p.a = 0xff;
+    *out = p;
+}
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/greyscale.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/greyscale.rs
new file mode 100644
index 0000000..b5abf3f0
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/greyscale.rs
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+#pragma rs_fp_relaxed
+
+const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
+
+void root(const uchar4 *v_in, uchar4 *v_out) {
+    float4 f4 = rsUnpackColor8888(*v_in);
+
+    float3 mono = dot(f4.rgb, gMonoMult);
+    *v_out = rsPackColorTo8888(mono);
+}
+
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/horizontal_blur.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/horizontal_blur.rs
new file mode 100644
index 0000000..ee83496
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/horizontal_blur.rs
@@ -0,0 +1,28 @@
+#pragma version(1)
+#pragma rs_fp_relaxed
+
+#include "ip.rsh"
+
+void root(float4 *out, const void *usrData, uint32_t x, uint32_t y) {
+    const FilterStruct *fs = (const FilterStruct *)usrData;
+    float3 blurredPixel = 0;
+    const float *gPtr = fs->gaussian;
+    if ((x > fs->radius) && (x < (fs->width - fs->radius))) {
+        for (int r = -fs->radius; r <= fs->radius; r ++) {
+            const float4 *i = (const float4 *)rsGetElementAt(fs->ain, x + r, y);
+            blurredPixel += i->xyz * gPtr[0];
+            gPtr++;
+        }
+    } else {
+        for (int r = -fs->radius; r <= fs->radius; r ++) {
+            // Stepping left and right away from the pixel
+            int validX = rsClamp((int)x + r, (int)0, (int)(fs->width - 1));
+            const float4 *i = (const float4 *)rsGetElementAt(fs->ain, validX, y);
+            blurredPixel += i->xyz * gPtr[0];
+            gPtr++;
+        }
+    }
+
+    out->xyz = blurredPixel;
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip.rsh b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip.rsh
new file mode 100644
index 0000000..0cdf9e1
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip.rsh
@@ -0,0 +1,15 @@
+#pragma rs java_package_name(com.android.rs.image2)
+
+#define MAX_RADIUS 25
+
+typedef struct FilterStruct_s {
+    rs_allocation ain;
+
+    float *gaussian; //[MAX_RADIUS * 2 + 1];
+    int height;
+    int width;
+    int radius;
+
+} FilterStruct;
+
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels.rsh b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels.rsh
new file mode 100644
index 0000000..7c5d930
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels.rsh
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+float inBlack;
+float outBlack;
+float inWMinInB;
+float outWMinOutB;
+float overInWMinInB;
+rs_matrix3x3 colorMat;
+
+void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
+    float3 pixel = convert_float4(in[0]).rgb;
+    pixel = rsMatrixMultiply(&colorMat, pixel);
+    pixel = clamp(pixel, 0.f, 255.f);
+    pixel = (pixel - inBlack) * overInWMinInB;
+    pixel = pixel * outWMinOutB + outBlack;
+    pixel = clamp(pixel, 0.f, 255.f);
+    out->xyz = convert_uchar3(pixel);
+    out->w = 0xff;
+}
+
+void root4(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
+    float4 pixel = convert_float4(in[0]);
+    pixel.rgb = rsMatrixMultiply(&colorMat, pixel.rgb);
+    pixel = clamp(pixel, 0.f, 255.f);
+    pixel = (pixel - inBlack) * overInWMinInB;
+    pixel = pixel * outWMinOutB + outBlack;
+    pixel = clamp(pixel, 0.f, 255.f);
+    out->xyzw = convert_uchar4(pixel);
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_full.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_full.rs
new file mode 100644
index 0000000..a4aa388
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_full.rs
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+
+#include "levels.rsh"
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs
new file mode 100644
index 0000000..ffdcfe3
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/levels_relaxed.rs
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+#pragma rs_fp_relaxed
+
+#include "levels.rsh"
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/threshold.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/threshold.rs
new file mode 100644
index 0000000..77cd5be
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/threshold.rs
@@ -0,0 +1,93 @@
+#pragma version(1)
+
+#include "ip.rsh"
+
+int height;
+int width;
+int radius;
+
+uchar4 * InPixel;
+uchar4 * OutPixel;
+float4 * ScratchPixel1;
+float4 * ScratchPixel2;
+
+rs_script vBlurScript;
+rs_script hBlurScript;
+
+const int CMD_FINISHED = 1;
+
+// Store our coefficients here
+static float gaussian[MAX_RADIUS * 2 + 1];
+
+
+static void computeGaussianWeights() {
+    // Compute gaussian weights for the blur
+    // e is the euler's number
+    float e = 2.718281828459045f;
+    float pi = 3.1415926535897932f;
+    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
+    // x is of the form [-radius .. 0 .. radius]
+    // and sigma varies with radius.
+    // Based on some experimental radius values and sigma's
+    // we approximately fit sigma = f(radius) as
+    // sigma = radius * 0.4  + 0.6
+    // The larger the radius gets, the more our gaussian blur
+    // will resemble a box blur since with large sigma
+    // the gaussian curve begins to lose its shape
+    float sigma = 0.4f * (float)radius + 0.6f;
+
+    // Now compute the coefficints
+    // We will store some redundant values to save some math during
+    // the blur calculations
+    // precompute some values
+    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
+    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
+
+    float normalizeFactor = 0.0f;
+    float floatR = 0.0f;
+    int r;
+    for (r = -radius; r <= radius; r ++) {
+        floatR = (float)r;
+        gaussian[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
+        normalizeFactor += gaussian[r + radius];
+    }
+
+    //Now we need to normalize the weights because all our coefficients need to add up to one
+    normalizeFactor = 1.0f / normalizeFactor;
+    for (r = -radius; r <= radius; r ++) {
+        floatR = (float)r;
+        gaussian[r + radius] *= normalizeFactor;
+    }
+}
+
+
+static void copyInput() {
+    rs_allocation ain;
+    ain = rsGetAllocation(InPixel);
+    uint32_t dimx = rsAllocationGetDimX(ain);
+    uint32_t dimy = rsAllocationGetDimY(ain);
+    for (uint32_t y = 0; y < dimy; y++) {
+        for (uint32_t x = 0; x < dimx; x++) {
+            ScratchPixel1[x + y * dimx] = convert_float4(InPixel[x + y * dimx]);
+        }
+    }
+}
+
+void filter() {
+    copyInput();
+    computeGaussianWeights();
+
+    FilterStruct fs;
+    fs.gaussian = gaussian;
+    fs.width = width;
+    fs.height = height;
+    fs.radius = radius;
+
+    fs.ain = rsGetAllocation(ScratchPixel1);
+    rsForEach(hBlurScript, fs.ain, rsGetAllocation(ScratchPixel2), &fs, sizeof(fs));
+
+    fs.ain = rsGetAllocation(ScratchPixel2);
+    rsForEach(vBlurScript, fs.ain, rsGetAllocation(OutPixel), &fs, sizeof(fs));
+    //rsSendToClientBlocking(CMD_FINISHED);
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vertical_blur.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vertical_blur.rs
new file mode 100644
index 0000000..60fd71b
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vertical_blur.rs
@@ -0,0 +1,59 @@
+#pragma version(1)
+#pragma rs_fp_relaxed
+
+#include "ip.rsh"
+
+static float saturation;
+static rs_matrix3x3 colorMat;
+
+void setSaturation(float sat) {
+    saturation = sat;
+
+    // Saturation
+    // Linear weights
+    //float rWeight = 0.3086f;
+    //float gWeight = 0.6094f;
+    //float bWeight = 0.0820f;
+
+    // Gamma 2.2 weights (we haven't converted our image to linear space yet for perf reasons)
+    float rWeight = 0.299f;
+    float gWeight = 0.587f;
+    float bWeight = 0.114f;
+
+    float oneMinusS = 1.0f - saturation;
+    rsMatrixSet(&colorMat, 0, 0, oneMinusS * rWeight + saturation);
+    rsMatrixSet(&colorMat, 0, 1, oneMinusS * rWeight);
+    rsMatrixSet(&colorMat, 0, 2, oneMinusS * rWeight);
+    rsMatrixSet(&colorMat, 1, 0, oneMinusS * gWeight);
+    rsMatrixSet(&colorMat, 1, 1, oneMinusS * gWeight + saturation);
+    rsMatrixSet(&colorMat, 1, 2, oneMinusS * gWeight);
+    rsMatrixSet(&colorMat, 2, 0, oneMinusS * bWeight);
+    rsMatrixSet(&colorMat, 2, 1, oneMinusS * bWeight);
+    rsMatrixSet(&colorMat, 2, 2, oneMinusS * bWeight + saturation);
+}
+
+void root(uchar4 *out, const void *usrData, uint32_t x, uint32_t y) {
+    const FilterStruct *fs = (const FilterStruct *)usrData;
+    float3 blurredPixel = 0;
+    const float *gPtr = fs->gaussian;
+    if ((y > fs->radius) && (y < (fs->height - fs->radius))) {
+        for (int r = -fs->radius; r <= fs->radius; r ++) {
+            const float4 *i = (const float4 *)rsGetElementAt(fs->ain, x, y + r);
+            blurredPixel += i->xyz * gPtr[0];
+            gPtr++;
+        }
+    } else {
+        for (int r = -fs->radius; r <= fs->radius; r ++) {
+            int validH = rsClamp((int)y + r, (int)0, (int)(fs->height - 1));
+            const float4 *i = (const float4 *)rsGetElementAt(fs->ain, x, validH);
+            blurredPixel += i->xyz * gPtr[0];
+            gPtr++;
+        }
+    }
+
+    float3 temp = rsMatrixMultiply(&colorMat, blurredPixel);
+    temp = clamp(temp, 0.f, 255.f);
+    out->xyz = convert_uchar3(temp);
+    out->w = 0xff;
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette.rsh b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette.rsh
new file mode 100644
index 0000000..a1e4ae5
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette.rsh
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+static float2 neg_center, axis_scale, inv_dimensions;
+static float sloped_neg_range, sloped_inv_max_dist, shade, opp_shade;
+
+void init_vignette(uint32_t dim_x, uint32_t dim_y, float center_x, float center_y,
+        float desired_scale, float desired_shade, float desired_slope) {
+
+    neg_center.x = -center_x;
+    neg_center.y = -center_y;
+    inv_dimensions.x = 1.f / (float)dim_x;
+    inv_dimensions.y = 1.f / (float)dim_y;
+
+    axis_scale = (float2)1.f;
+    if (dim_x > dim_y)
+        axis_scale.y = (float)dim_y / (float)dim_x;
+    else
+        axis_scale.x = (float)dim_x / (float)dim_y;
+
+    const float max_dist = 0.5 * length(axis_scale);
+    sloped_inv_max_dist = desired_slope * 1.f/max_dist;
+
+    // Range needs to be between 1.3 to 0.6. When scale is zero then range is
+    // 1.3 which means no vignette at all because the luminousity difference is
+    // less than 1/256.  Expect input scale to be between 0.0 and 1.0.
+    const float neg_range = 0.7*sqrt(desired_scale) - 1.3;
+    sloped_neg_range = exp(neg_range * desired_slope);
+
+    shade = desired_shade;
+    opp_shade = 1.f - desired_shade;
+}
+
+void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
+    // Convert x and y to floating point coordinates with center as origin
+    const float4 fin = convert_float4(*in);
+    const float2 inCoord = {(float)x, (float)y};
+    const float2 coord = mad(inCoord, inv_dimensions, neg_center);
+    const float sloped_dist_ratio = length(axis_scale * coord)  * sloped_inv_max_dist;
+    const float lumen = opp_shade + shade / ( 1.0 + sloped_neg_range * exp(sloped_dist_ratio) );
+    float4 fout;
+    fout.rgb = fin.rgb * lumen;
+    fout.w = fin.w;
+    *out = convert_uchar4(fout);
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_full.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_full.rs
new file mode 100644
index 0000000..5fc2dda
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_full.rs
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+
+#include "vignette.rsh"
+
diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_relaxed.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_relaxed.rs
new file mode 100644
index 0000000..430b685
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_relaxed.rs
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image2)
+#pragma rs_fp_relaxed
+
+#include "vignette.rsh"
+
diff --git a/tests/StatusBar/src/com/android/statusbartest/PowerTest.java b/tests/StatusBar/src/com/android/statusbartest/PowerTest.java
index 31a1cf5a..e38bb6c 100644
--- a/tests/StatusBar/src/com/android/statusbartest/PowerTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/PowerTest.java
@@ -16,32 +16,15 @@
 
 package com.android.statusbartest;
 
-import android.app.ListActivity;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.widget.ArrayAdapter;
-import android.view.View;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.IPowerManager;
-import android.widget.ListView;
-import android.content.Intent;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.StatusBarManager;
 import android.os.RemoteException;
-import android.os.Vibrator;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.LocalPowerManager;
 import android.os.ServiceManager;
-import android.util.Log;
-import android.net.Uri;
-import android.os.SystemClock;
-import android.widget.RemoteViews;
-import android.widget.Toast;
 import android.os.PowerManager;
 
 public class PowerTest extends TestActivity
@@ -101,6 +84,28 @@
                 mProx.release(PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE);
             }
         },
+        new Test("Enable proximity, wait 5 seconds then disable") {
+            public void run() {
+                mProx.acquire();
+                mHandler.postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        mProx.release();
+                    }
+                }, 5000);
+            }
+        },
+        new Test("Enable proximity, wait 5 seconds then disable  (WAIT_FOR_PROXIMITY_NEGATIVE)") {
+            public void run() {
+                mProx.acquire();
+                mHandler.postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        mProx.release(PowerManager.WAIT_FOR_PROXIMITY_NEGATIVE);
+                    }
+                }, 5000);
+            }
+        },
         new Test("Touch events don't poke") {
             public void run() {
                 mPokeState |= LocalPowerManager.POKE_LOCK_IGNORE_TOUCH_EVENTS;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index c4a6906..292e4fc 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -440,7 +440,7 @@
         }
 
         if (POWER_SERVICE.equals(service)) {
-            return new PowerManager(new BridgePowerManager(), new Handler());
+            return new PowerManager(this, new BridgePowerManager(), new Handler());
         }
 
         throw new UnsupportedOperationException("Unsupported Service: " + service);
@@ -917,6 +917,12 @@
     }
 
     @Override
+    public Context createConfigurationContext(Configuration overrideConfiguration) {
+        // pass
+        return null;
+    }
+
+    @Override
     public String[] databaseList() {
         // pass
         return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
index 6071a6b..0c85204 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
@@ -39,7 +39,7 @@
     }
 
     @Override
-    public void acquireWakeLock(int arg0, IBinder arg1, String arg2, WorkSource arg3)
+    public void acquireWakeLock(IBinder arg0, int arg1, String arg2, WorkSource arg3)
             throws RemoteException {
         // pass for now.
     }
@@ -55,18 +55,7 @@
     }
 
     @Override
-    public int getSupportedWakeLockFlags() throws RemoteException {
-        // pass for now.
-        return 0;
-    }
-
-    @Override
-    public void goToSleep(long arg0) throws RemoteException {
-        // pass for now.
-    }
-
-    @Override
-    public void goToSleepWithReason(long arg0, int arg1) throws RemoteException {
+    public void goToSleep(long arg0, int arg1) throws RemoteException {
         // pass for now.
     }
 
@@ -91,17 +80,17 @@
     }
 
     @Override
-    public void setAutoBrightnessAdjustment(float arg0) throws RemoteException {
+    public void setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(float arg0) throws RemoteException {
         // pass for now.
     }
 
     @Override
-    public void setBacklightBrightness(int arg0) throws RemoteException {
+    public void setTemporaryScreenBrightnessSettingOverride(int arg0) throws RemoteException {
         // pass for now.
     }
 
     @Override
-    public void setMaximumScreenOffTimeount(int arg0) throws RemoteException {
+    public void setMaximumScreenOffTimeoutFromDeviceAdmin(int arg0) throws RemoteException {
         // pass for now.
     }
 
@@ -121,12 +110,18 @@
     }
 
     @Override
-    public void userActivity(long arg0, boolean arg1) throws RemoteException {
+    public boolean isWakeLockLevelSupported(int level) throws RemoteException {
+        // pass for now.
+        return true;
+    }
+
+    @Override
+    public void userActivity(long time, int event, int flags) throws RemoteException {
         // pass for now.
     }
 
     @Override
-    public void userActivityWithForce(long arg0, boolean arg1, boolean arg2) throws RemoteException {
+    public void wakeUp(long time) throws RemoteException {
         // pass for now.
     }
 }
diff --git a/wifi/java/android/net/wifi/RssiPacketCountInfo.java b/wifi/java/android/net/wifi/RssiPacketCountInfo.java
new file mode 100644
index 0000000..f549e1d
--- /dev/null
+++ b/wifi/java/android/net/wifi/RssiPacketCountInfo.java
@@ -0,0 +1,71 @@
+/*
+ * 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 android.net.wifi;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Bundle of RSSI and packet count information, for WiFi watchdog
+ *
+ * @see WifiWatchdogStateMachine
+ *
+ * @hide
+ */
+public class RssiPacketCountInfo implements Parcelable {
+
+    public int rssi;
+
+    public int txgood;
+
+    public int txbad;
+
+    public RssiPacketCountInfo() {
+        rssi = txgood = txbad = 0;
+    }
+
+    private RssiPacketCountInfo(Parcel in) {
+        rssi = in.readInt();
+        txgood = in.readInt();
+        txbad = in.readInt();
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(rssi);
+        out.writeInt(txgood);
+        out.writeInt(txbad);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<RssiPacketCountInfo> CREATOR =
+            new Parcelable.Creator<RssiPacketCountInfo>() {
+        @Override
+        public RssiPacketCountInfo createFromParcel(Parcel in) {
+            return new RssiPacketCountInfo(in);
+        }
+
+        @Override
+        public RssiPacketCountInfo[] newArray(int size) {
+            return new RssiPacketCountInfo[size];
+        }
+    };
+}
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 3e20756..32261de 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -47,37 +47,19 @@
     public int frequency;
 
     /**
-     * Time Synchronization Function (tsf) timestamp in microseconds when
-     * this result was last seen.
-     */
-     public long timestamp;
-
-    /**
      * We'd like to obtain the following attributes,
      * but they are not reported via the socket
      * interface, even though they are known
      * internally by wpa_supplicant.
      * {@hide}
      */
-    public ScanResult(String SSID, String BSSID, String caps, int level, int frequency, long tsf) {
+    public ScanResult(String SSID, String BSSID, String caps, int level, int frequency) {
         this.SSID = SSID;
         this.BSSID = BSSID;
         this.capabilities = caps;
         this.level = level;
         this.frequency = frequency;
-        this.timestamp = tsf;
-    }
-
-    /** copy constructor {@hide} */
-    public ScanResult(ScanResult source) {
-        if (source != null) {
-            SSID = source.SSID;
-            BSSID = source.BSSID;
-            capabilities = source.capabilities;
-            level = source.level;
-            frequency = source.frequency;
-            timestamp = source.timestamp;
-        }
+        //networkConfig = null;
     }
 
     @Override
@@ -94,9 +76,7 @@
             append(", level: ").
             append(level).
             append(", frequency: ").
-            append(frequency).
-            append(", timestamp: ").
-            append(timestamp);
+            append(frequency);
 
         return sb.toString();
     }
@@ -113,7 +93,6 @@
         dest.writeString(capabilities);
         dest.writeInt(level);
         dest.writeInt(frequency);
-        dest.writeLong(timestamp);
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -125,8 +104,7 @@
                     in.readString(),
                     in.readString(),
                     in.readInt(),
-                    in.readInt(),
-                    in.readLong()
+                    in.readInt()
                 );
             }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 6e58a2d..3579b86 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -906,6 +906,17 @@
     }
 
     /**
+     * Return TX packet counter, for CTS test of WiFi watchdog.
+     * @param listener is the interface to receive result
+     *
+     * @hide for CTS test only
+     */
+    public void getTxPacketCount(TxPacketCountListener listener) {
+        validateChannel();
+        mAsyncChannel.sendMessage(RSSI_PKTCNT_FETCH, 0, putListener(listener));
+    }
+
+    /**
      * Calculates the level of the signal. This should be used any time a signal
      * is being shown.
      *
@@ -1143,11 +1154,18 @@
     /** @hide */
     public static final int DISABLE_NETWORK_SUCCEEDED       = BASE + 19;
 
+    /** @hide */
+    public static final int RSSI_PKTCNT_FETCH               = BASE + 20;
+    /** @hide */
+    public static final int RSSI_PKTCNT_FETCH_SUCCEEDED     = BASE + 21;
+    /** @hide */
+    public static final int RSSI_PKTCNT_FETCH_FAILED        = BASE + 22;
+
     /* For system use only */
     /** @hide */
-    public static final int ENABLE_TRAFFIC_STATS_POLL       = BASE + 21;
+    public static final int ENABLE_TRAFFIC_STATS_POLL       = BASE + 31;
     /** @hide */
-    public static final int TRAFFIC_STATS_POLL              = BASE + 22;
+    public static final int TRAFFIC_STATS_POLL              = BASE + 32;
 
 
     /**
@@ -1212,6 +1230,21 @@
         public void onFailure(int reason);
     }
 
+    /** Interface for callback invocation on a TX packet count poll action {@hide} */
+    public interface TxPacketCountListener {
+        /**
+         * The operation succeeded
+         * @param count TX packet counter
+         */
+        public void onSuccess(int count);
+        /**
+         * The operation failed
+         * @param reason The reason for failure could be one of
+         * {@link #ERROR}, {@link #IN_PROGRESS} or {@link #BUSY}
+         */
+        public void onFailure(int reason);
+    }
+
     private class ServiceHandler extends Handler {
         ServiceHandler(Looper looper) {
             super(looper);
@@ -1281,6 +1314,20 @@
                         ((WpsListener) listener).onFailure(message.arg1);
                     }
                     break;
+                case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED:
+                    if (listener != null) {
+                        RssiPacketCountInfo info = (RssiPacketCountInfo) message.obj;
+                        if (info != null)
+                            ((TxPacketCountListener) listener).onSuccess(info.txgood + info.txbad);
+                        else
+                            ((TxPacketCountListener) listener).onFailure(ERROR);
+                    }
+                    break;
+                case WifiManager.RSSI_PKTCNT_FETCH_FAILED:
+                    if (listener != null) {
+                        ((TxPacketCountListener) listener).onFailure(message.arg1);
+                    }
+                    break;
                 default:
                     //ignore
                     break;
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 1b7e378..84c565b0 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -197,22 +197,8 @@
         return null;
     }
 
-    /**
-     * Format of results:
-     * =================
-     * bssid=68:7f:74:d7:1b:6e
-     * freq=2412
-     * level=-43
-     * tsf=1344621975160944
-     * age=2623
-     * flags=[WPA2-PSK-CCMP][WPS][ESS]
-     * ssid=zubyb
-     *
-     * RANGE=ALL gets all scan results
-     * MASK=<N> see wpa_supplicant/src/common/wpa_ctrl.h for details
-     */
     public String scanResults() {
-        return doStringCommand("BSS RANGE=ALL MASK=0x1986");
+        return doStringCommand("SCAN_RESULTS");
     }
 
     public boolean startDriver() {
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 1b7894e..6abca65 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -52,7 +52,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkUtils;
-import android.net.wifi.WifiWatchdogStateMachine.RssiPktcntStat;
+import android.net.wifi.RssiPacketCountInfo;
 import android.net.wifi.WpsResult.Status;
 import android.net.wifi.p2p.WifiP2pManager;
 import android.net.wifi.p2p.WifiP2pService;
@@ -891,13 +891,7 @@
      * TODO: doc
      */
     public List<ScanResult> syncGetScanResultsList() {
-        synchronized (mScanResultCache) {
-            List<ScanResult> scanList = new ArrayList<ScanResult>();
-            for(ScanResult result: mScanResults) {
-                scanList.add(new ScanResult(result));
-            }
-            return scanList;
-        }
+        return mScanResults;
     }
 
     /**
@@ -1195,7 +1189,7 @@
             case CMD_RSSI_POLL:
             case CMD_DELAYED_STOP_DRIVER:
             case WifiMonitor.SCAN_RESULTS_EVENT:
-            case WifiWatchdogStateMachine.RSSI_PKTCNT_FETCH:
+            case WifiManager.RSSI_PKTCNT_FETCH:
                 return false;
             default:
                 return true;
@@ -1261,14 +1255,14 @@
            ip settings */
         InterfaceConfiguration ifcg = null;
         try {
-            ifcg = mNwService.getInterfaceConfig(mInterfaceName);
+            ifcg = mNwService.getInterfaceConfig(mTetherInterfaceName);
             if (ifcg != null) {
                 ifcg.setLinkAddress(
                         new LinkAddress(NetworkUtils.numericToInetAddress("0.0.0.0"), 0));
-                mNwService.setInterfaceConfig(mInterfaceName, ifcg);
+                mNwService.setInterfaceConfig(mTetherInterfaceName, ifcg);
             }
         } catch (Exception e) {
-            loge("Error resetting interface " + mInterfaceName + ", :" + e);
+            loge("Error resetting interface " + mTetherInterfaceName + ", :" + e);
         }
 
         if (mCm.untether(mTetherInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
@@ -1363,103 +1357,131 @@
         mContext.sendStickyBroadcast(intent);
     }
 
-    private static final String BSSID_STR = "bssid=";
-    private static final String FREQ_STR = "freq=";
-    private static final String LEVEL_STR = "level=";
-    private static final String TSF_STR = "tsf=";
-    private static final String FLAGS_STR = "flags=";
-    private static final String SSID_STR = "ssid=";
-    private static final String DELIMITER_STR = "====";
     /**
-     * Format:
-     * bssid=68:7f:76:d7:1a:6e
-     * freq=2412
-     * level=-44
-     * tsf=1344626243700342
-     * flags=[WPA2-PSK-CCMP][WPS][ESS]
-     * ssid=zfdy
-     * ====
-     * bssid=68:5f:74:d7:1a:6f
-     * freq=5180
-     * level=-73
-     * tsf=1344626243700373
-     * flags=[WPA2-PSK-CCMP][WPS][ESS]
-     * ssid=zuby
-     * ====
+     * Parse the scan result line passed to us by wpa_supplicant (helper).
+     * @param line the line to parse
+     * @return the {@link ScanResult} object
      */
-    private void setScanResults(String scanResults) {
-        String bssid = "";
-        int level = 0;
-        int freq = 0;
-        long tsf = 0;
-        String flags = "";
-        String ssid = "";
-
-        if (scanResults == null) {
-            return;
-        }
-
-        synchronized(mScanResultCache) {
-            mScanResults = new ArrayList<ScanResult>();
-            String[] lines = scanResults.split("\n");
-
-            for (String line : lines) {
-                if (line.startsWith(BSSID_STR)) {
-                    bssid = line.substring(BSSID_STR.length());
-                } else if (line.startsWith(FREQ_STR)) {
+    private ScanResult parseScanResult(String line) {
+        ScanResult scanResult = null;
+        if (line != null) {
+            /*
+             * Cache implementation (LinkedHashMap) is not synchronized, thus,
+             * must synchronized here!
+             */
+            synchronized (mScanResultCache) {
+                String[] result = scanResultPattern.split(line);
+                if (3 <= result.length && result.length <= 5) {
+                    String bssid = result[0];
+                    // bssid | frequency | level | flags | ssid
+                    int frequency;
+                    int level;
                     try {
-                        freq = Integer.parseInt(line.substring(FREQ_STR.length()));
-                    } catch (NumberFormatException e) {
-                        freq = 0;
-                    }
-                } else if (line.startsWith(LEVEL_STR)) {
-                    try {
-                        level = Integer.parseInt(line.substring(LEVEL_STR.length()));
+                        frequency = Integer.parseInt(result[1]);
+                        level = Integer.parseInt(result[2]);
                         /* some implementations avoid negative values by adding 256
                          * so we need to adjust for that here.
                          */
                         if (level > 0) level -= 256;
-                    } catch(NumberFormatException e) {
+                    } catch (NumberFormatException e) {
+                        frequency = 0;
                         level = 0;
                     }
-                } else if (line.startsWith(TSF_STR)) {
-                    try {
-                        tsf = Long.parseLong(line.substring(TSF_STR.length()));
-                    } catch (NumberFormatException e) {
-                        tsf = 0;
-                    }
-                } else if (line.startsWith(FLAGS_STR)) {
-                    flags = line.substring(FLAGS_STR.length());
-                } else if (line.startsWith(SSID_STR)) {
-                    ssid = line.substring(SSID_STR.length());
-                    if (ssid == null) ssid = "";
-                } else if (line.startsWith(DELIMITER_STR)) {
-                    if (bssid != null) {
-                        String key = bssid + ssid;
-                        ScanResult scanResult = mScanResultCache.get(key);
-                        if (scanResult != null) {
-                            scanResult.level = level;
-                            scanResult.SSID = ssid;
-                            scanResult.capabilities = flags;
-                            scanResult.frequency = freq;
-                            scanResult.timestamp = tsf;
+
+                    /*
+                     * The formatting of the results returned by
+                     * wpa_supplicant is intended to make the fields
+                     * line up nicely when printed,
+                     * not to make them easy to parse. So we have to
+                     * apply some heuristics to figure out which field
+                     * is the SSID and which field is the flags.
+                     */
+                    String ssid;
+                    String flags;
+                    if (result.length == 4) {
+                        if (result[3].charAt(0) == '[') {
+                            flags = result[3];
+                            ssid = "";
                         } else {
+                            flags = "";
+                            ssid = result[3];
+                        }
+                    } else if (result.length == 5) {
+                        flags = result[3];
+                        ssid = result[4];
+                    } else {
+                        // Here, we must have 3 fields: no flags and ssid
+                        // set
+                        flags = "";
+                        ssid = "";
+                    }
+
+                    // bssid + ssid is the hash key
+                    String key = bssid + ssid;
+                    scanResult = mScanResultCache.get(key);
+                    if (scanResult != null) {
+                        scanResult.level = level;
+                        scanResult.SSID = ssid;
+                        scanResult.capabilities = flags;
+                        scanResult.frequency = frequency;
+                    } else {
+                        // Do not add scan results that have no SSID set
+                        if (0 < ssid.trim().length()) {
                             scanResult =
                                 new ScanResult(
-                                        ssid, bssid, flags, level, freq, tsf);
+                                    ssid, bssid, flags, level, frequency);
                             mScanResultCache.put(key, scanResult);
                         }
-                        mScanResults.add(scanResult);
                     }
-                    bssid = null;
-                    level = 0;
-                    freq = 0;
-                    tsf = 0;
-                    flags = "";
-                    ssid = "";
+                } else {
+                    loge("Misformatted scan result text with " +
+                          result.length + " fields: " + line);
                 }
             }
         }
+
+        return scanResult;
+    }
+
+    /**
+     * scanResults input format
+     * 00:bb:cc:dd:cc:ee       2427    166     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net1
+     * 00:bb:cc:dd:cc:ff       2412    165     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net2
+     */
+    private void setScanResults(String scanResults) {
+        if (scanResults == null) {
+            return;
+        }
+
+        List<ScanResult> scanList = new ArrayList<ScanResult>();
+
+        int lineCount = 0;
+
+        int scanResultsLen = scanResults.length();
+        // Parse the result string, keeping in mind that the last line does
+        // not end with a newline.
+        for (int lineBeg = 0, lineEnd = 0; lineEnd <= scanResultsLen; ++lineEnd) {
+            if (lineEnd == scanResultsLen || scanResults.charAt(lineEnd) == '\n') {
+                ++lineCount;
+
+                if (lineCount == 1) {
+                    lineBeg = lineEnd + 1;
+                    continue;
+                }
+                if (lineEnd > lineBeg) {
+                    String line = scanResults.substring(lineBeg, lineEnd);
+                    ScanResult scanResult = parseScanResult(line);
+                    if (scanResult != null) {
+                        scanList.add(scanResult);
+                    } else {
+                        //TODO: hidden network handling
+                    }
+                }
+                lineBeg = lineEnd + 1;
+            }
+        }
+
+        mScanResults = scanList;
     }
 
     /*
@@ -1521,7 +1543,7 @@
     /*
      * Fetch TX packet counters on current connection
      */
-    private void fetchPktcntNative(RssiPktcntStat stat) {
+    private void fetchPktcntNative(RssiPacketCountInfo info) {
         String pktcntPoll = mWifiNative.pktcntPoll();
 
         if (pktcntPoll != null) {
@@ -1531,9 +1553,9 @@
                 if (prop.length < 2) continue;
                 try {
                     if (prop[0].equals("TXGOOD")) {
-                        stat.txgood = Integer.parseInt(prop[1]);
+                        info.txgood = Integer.parseInt(prop[1]);
                     } else if (prop[0].equals("TXBAD")) {
-                        stat.txbad = Integer.parseInt(prop[1]);
+                        info.txbad = Integer.parseInt(prop[1]);
                     }
                 } catch (NumberFormatException e) {
                     //Ignore
@@ -1950,8 +1972,9 @@
                     replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
                             WifiManager.BUSY);
                     break;
-                case WifiWatchdogStateMachine.RSSI_PKTCNT_FETCH:
-                    replyToMessage(message, WifiWatchdogStateMachine.RSSI_PKTCNT_FETCH_FAILED);
+                case WifiManager.RSSI_PKTCNT_FETCH:
+                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED,
+                            WifiManager.BUSY);
                     break;
                 default:
                     loge("Error! unhandled message" + message);
@@ -2805,7 +2828,7 @@
             if (DBG) log(getName() + "\n");
             mIsRunning = false;
             updateBatteryWorkSource(null);
-            mScanResults = new ArrayList<ScanResult>();
+            mScanResults = null;
 
             if (mP2pSupported) mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P);
             mContext.unregisterReceiver(mScreenReceiver);
@@ -3154,13 +3177,12 @@
                                 mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
                     }
                     break;
-                case WifiWatchdogStateMachine.RSSI_PKTCNT_FETCH:
-                    RssiPktcntStat stat = (RssiPktcntStat) message.obj;
+                case WifiManager.RSSI_PKTCNT_FETCH:
+                    RssiPacketCountInfo info = new RssiPacketCountInfo();
                     fetchRssiAndLinkSpeedNative();
-                    stat.rssi = mWifiInfo.getRssi();
-                    fetchPktcntNative(stat);
-                    replyToMessage(message, WifiWatchdogStateMachine.RSSI_PKTCNT_FETCH_SUCCEEDED,
-                            stat);
+                    info.rssi = mWifiInfo.getRssi();
+                    fetchPktcntNative(info);
+                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info);
                     break;
                 default:
                     return NOT_HANDLED;
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index 7b4d113..29a53b6 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -30,6 +30,7 @@
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
 import android.net.Uri;
+import android.net.wifi.RssiPacketCountInfo;
 import android.os.Message;
 import android.os.SystemClock;
 import android.provider.Settings;
@@ -105,9 +106,6 @@
     /* Notifications from/to WifiStateMachine */
     static final int POOR_LINK_DETECTED                             = BASE + 21;
     static final int GOOD_LINK_DETECTED                             = BASE + 22;
-    static final int RSSI_PKTCNT_FETCH                              = BASE + 23;
-    static final int RSSI_PKTCNT_FETCH_SUCCEEDED                    = BASE + 24;
-    static final int RSSI_PKTCNT_FETCH_FAILED                       = BASE + 25;
 
     /*
      * RSSI levels as used by notification icon
@@ -123,7 +121,7 @@
      * <p>
      * Larger threshold is more adaptive but increases sampling cost.
      */
-    private static final int LINK_MONITOR_LEVEL_THRESHOLD = 4;
+    private static final int LINK_MONITOR_LEVEL_THRESHOLD = WifiManager.RSSI_LEVELS - 1;
 
     /**
      * Remember packet loss statistics of how many BSSIDs.
@@ -228,8 +226,8 @@
      * Adaptive good link target to avoid flapping.
      * When a poor link is detected, a good link target is calculated as follows:
      * <p>
-     *      targetRSSI = min{ rssi | loss(rssi) < GOOD_LINK_LOSS_THRESHOLD } + rssi_adj[i],
-     *                   where rssi is in the above GOOD_LINK_RSSI_RANGE.
+     *      targetRSSI = min { rssi | loss(rssi) < GOOD_LINK_LOSS_THRESHOLD } + rssi_adj[i],
+     *                   where rssi is within the above GOOD_LINK_RSSI_RANGE.
      *      targetCount = sample_count[i] .
      * <p>
      * While WiFi is being avoided, we keep monitoring its signal strength.
@@ -241,7 +239,7 @@
      * <p>
      * Intuitively, larger index i makes it more difficult to get back to WiFi, avoiding flapping.
      * In experiments, (+9 dB / 30 counts) makes it quite difficult to achieve.
-     * Avoid using it unless flapping is really bad (say, last poor link is only 1min ago).
+     * Avoid using it unless flapping is really bad (say, last poor link is < 1 min ago).
      */
     private static final GoodLinkTarget[] GOOD_LINK_TARGET = {
         /*                  rssi_adj,       sample_count,   reduce_time */
@@ -591,8 +589,8 @@
                 case EVENT_BSSID_CHANGE:
                 case CMD_DELAYED_WALLED_GARDEN_CHECK:
                 case CMD_RSSI_FETCH:
-                case RSSI_PKTCNT_FETCH_SUCCEEDED:
-                case RSSI_PKTCNT_FETCH_FAILED:
+                case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED:
+                case WifiManager.RSSI_PKTCNT_FETCH_FAILED:
                     // ignore
                     break;
                 case EVENT_SCREEN_ON:
@@ -764,15 +762,15 @@
 
                 case CMD_RSSI_FETCH:
                     if (msg.arg1 == mRssiFetchToken) {
-                        mWsmChannel.sendMessage(RSSI_PKTCNT_FETCH, new RssiPktcntStat());
+                        mWsmChannel.sendMessage(WifiManager.RSSI_PKTCNT_FETCH);
                         sendMessageDelayed(obtainMessage(CMD_RSSI_FETCH, ++mRssiFetchToken, 0),
                                 LINK_SAMPLING_INTERVAL_MS);
                     }
                     break;
 
-                case RSSI_PKTCNT_FETCH_SUCCEEDED:
-                    RssiPktcntStat stat = (RssiPktcntStat) msg.obj;
-                    int rssi = stat.rssi;
+                case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED:
+                    RssiPacketCountInfo info = (RssiPacketCountInfo) msg.obj;
+                    int rssi = info.rssi;
                     if (DBG) logd("Fetch RSSI succeed, rssi=" + rssi);
 
                     long time = mCurrentBssid.mBssidAvoidTimeMax - SystemClock.elapsedRealtime();
@@ -795,7 +793,7 @@
                     }
                     break;
 
-                case RSSI_PKTCNT_FETCH_FAILED:
+                case WifiManager.RSSI_PKTCNT_FETCH_FAILED:
                     if (DBG) logd("RSSI_FETCH_FAILED");
                     break;
 
@@ -944,18 +942,18 @@
                     if (!mIsScreenOn) {
                         transitionTo(mOnlineState);
                     } else if (msg.arg1 == mRssiFetchToken) {
-                        mWsmChannel.sendMessage(RSSI_PKTCNT_FETCH, new RssiPktcntStat());
+                        mWsmChannel.sendMessage(WifiManager.RSSI_PKTCNT_FETCH);
                         sendMessageDelayed(obtainMessage(CMD_RSSI_FETCH, ++mRssiFetchToken, 0),
                                 LINK_SAMPLING_INTERVAL_MS);
                     }
                     break;
 
-                case RSSI_PKTCNT_FETCH_SUCCEEDED:
-                    RssiPktcntStat stat = (RssiPktcntStat) msg.obj;
-                    int rssi = stat.rssi;
+                case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED:
+                    RssiPacketCountInfo info = (RssiPacketCountInfo) msg.obj;
+                    int rssi = info.rssi;
                     int mrssi = (mLastRssi + rssi) / 2;
-                    int txbad = stat.txbad;
-                    int txgood = stat.txgood;
+                    int txbad = info.txbad;
+                    int txgood = info.txgood;
                     if (DBG) logd("Fetch RSSI succeed, rssi=" + rssi + " mrssi=" + mrssi + " txbad="
                             + txbad + " txgood=" + txgood);
 
@@ -1003,7 +1001,7 @@
                     mLastRssi = rssi;
                     break;
 
-                case RSSI_PKTCNT_FETCH_FAILED:
+                case WifiManager.RSSI_PKTCNT_FETCH_FAILED:
                     // can happen if we are waiting to get a disconnect notification
                     if (DBG) logd("RSSI_FETCH_FAILED");
                     break;
@@ -1159,15 +1157,6 @@
     }
 
     /**
-     * Bundle of RSSI and packet count information
-     */
-    public class RssiPktcntStat {
-        public int rssi;
-        public int txgood;
-        public int txbad;
-    }
-
-    /**
      * Bundle of good link count parameters
      */
     private static class GoodLinkTarget {