Merge "Make media scanner use new delete-parameter"
diff --git a/Android.mk b/Android.mk
index ace7b7c..5acfb86 100644
--- a/Android.mk
+++ b/Android.mk
@@ -94,7 +94,7 @@
 	core/java/android/bluetooth/IBluetoothHealthCallback.aidl \
 	core/java/android/bluetooth/IBluetoothPbap.aidl \
 	core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl \
-	core/java/android/content/ICancelationSignal.aidl \
+	core/java/android/content/ICancellationSignal.aidl \
 	core/java/android/content/IClipboard.aidl \
 	core/java/android/content/IContentService.aidl \
 	core/java/android/content/IIntentReceiver.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index fb334fc..d74d7b1 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -117,6 +117,8 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/fonts/DroidSans*)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/content)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/content)
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/api/current.txt b/api/current.txt
index 9e94a0d..574af6b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2349,6 +2349,15 @@
     method public void setPropertyName(java.lang.String);
   }
 
+  public class TimeAnimator extends android.animation.ValueAnimator {
+    ctor public TimeAnimator();
+    method public void setTimeListener(android.animation.TimeAnimator.TimeListener);
+  }
+
+  public static abstract interface TimeAnimator.TimeListener {
+    method public abstract void onTimeUpdate(android.animation.TimeAnimator, long, long);
+  }
+
   public abstract interface TimeInterpolator {
     method public abstract float getInterpolation(float);
   }
@@ -4714,15 +4723,15 @@
     method public final void setResultExtras(android.os.Bundle);
   }
 
-  public final class CancelationSignal {
-    ctor public CancelationSignal();
+  public final class CancellationSignal {
+    ctor public CancellationSignal();
     method public void cancel();
     method public boolean isCanceled();
-    method public void setOnCancelListener(android.content.CancelationSignal.OnCancelListener);
+    method public void setOnCancelListener(android.content.CancellationSignal.OnCancelListener);
     method public void throwIfCanceled();
   }
 
-  public static abstract interface CancelationSignal.OnCancelListener {
+  public static abstract interface CancellationSignal.OnCancelListener {
     method public abstract void onCancel();
   }
 
@@ -4845,7 +4854,7 @@
     method public android.os.ParcelFileDescriptor openPipeHelper(android.net.Uri, java.lang.String, android.os.Bundle, T, android.content.ContentProvider.PipeDataWriter<T>) throws java.io.FileNotFoundException;
     method public android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
     method public abstract android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
-    method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.content.CancelationSignal);
+    method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.content.CancellationSignal);
     method protected final void setPathPermissions(android.content.pm.PathPermission[]);
     method protected final void setReadPermission(java.lang.String);
     method protected final void setWritePermission(java.lang.String);
@@ -4869,7 +4878,7 @@
     method public android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException, android.os.RemoteException;
     method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException, android.os.RemoteException;
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) throws android.os.RemoteException;
-    method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.content.CancelationSignal) throws android.os.RemoteException;
+    method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.content.CancellationSignal) throws android.os.RemoteException;
     method public boolean release();
     method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
   }
@@ -4956,7 +4965,7 @@
     method public final java.io.OutputStream openOutputStream(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
     method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
-    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.content.CancelationSignal);
+    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.content.CancellationSignal);
     method public final void registerContentObserver(android.net.Uri, boolean, android.database.ContentObserver);
     method public static void removePeriodicSync(android.accounts.Account, java.lang.String, android.os.Bundle);
     method public static void removeStatusChangeListener(java.lang.Object);
@@ -7268,15 +7277,15 @@
     method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory);
     method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, android.database.DatabaseErrorHandler);
     method public android.database.Cursor query(boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String);
-    method public android.database.Cursor query(boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, android.content.CancelationSignal);
+    method public android.database.Cursor query(boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, android.content.CancellationSignal);
     method public android.database.Cursor query(java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String);
     method public android.database.Cursor query(java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     method public android.database.Cursor queryWithFactory(android.database.sqlite.SQLiteDatabase.CursorFactory, boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String);
-    method public android.database.Cursor queryWithFactory(android.database.sqlite.SQLiteDatabase.CursorFactory, boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, android.content.CancelationSignal);
+    method public android.database.Cursor queryWithFactory(android.database.sqlite.SQLiteDatabase.CursorFactory, boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, android.content.CancellationSignal);
     method public android.database.Cursor rawQuery(java.lang.String, java.lang.String[]);
-    method public android.database.Cursor rawQuery(java.lang.String, java.lang.String[], android.content.CancelationSignal);
+    method public android.database.Cursor rawQuery(java.lang.String, java.lang.String[], android.content.CancellationSignal);
     method public android.database.Cursor rawQueryWithFactory(android.database.sqlite.SQLiteDatabase.CursorFactory, java.lang.String, java.lang.String[], java.lang.String);
-    method public android.database.Cursor rawQueryWithFactory(android.database.sqlite.SQLiteDatabase.CursorFactory, java.lang.String, java.lang.String[], java.lang.String, android.content.CancelationSignal);
+    method public android.database.Cursor rawQueryWithFactory(android.database.sqlite.SQLiteDatabase.CursorFactory, java.lang.String, java.lang.String[], java.lang.String, android.content.CancellationSignal);
     method public static int releaseMemory();
     method public long replace(java.lang.String, java.lang.String, android.content.ContentValues);
     method public long replaceOrThrow(java.lang.String, java.lang.String, android.content.ContentValues) throws android.database.SQLException;
@@ -7398,7 +7407,7 @@
     method public java.lang.String getTables();
     method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String);
     method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String);
-    method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, android.content.CancelationSignal);
+    method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, android.content.CancellationSignal);
     method public void setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory);
     method public void setDistinct(boolean);
     method public void setProjectionMap(java.util.Map<java.lang.String, java.lang.String>);
@@ -10926,11 +10935,12 @@
   }
 
   public final class MediaRecorder.OutputFormat {
+    field public static final int AAC_ADTS = 6; // 0x6
     field public static final int AMR_NB = 3; // 0x3
     field public static final int AMR_WB = 4; // 0x4
     field public static final int DEFAULT = 0; // 0x0
     field public static final int MPEG_4 = 2; // 0x2
-    field public static final int RAW_AMR = 3; // 0x3
+    field public static final deprecated int RAW_AMR = 3; // 0x3
     field public static final int THREE_GPP = 1; // 0x1
   }
 
@@ -17505,6 +17515,7 @@
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DATA_ROAMING = "data_roaming";
     field public static final java.lang.String DEFAULT_INPUT_METHOD = "default_input_method";
+    field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
     field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned";
     field public static final java.lang.String ENABLED_ACCESSIBILITY_SERVICES = "enabled_accessibility_services";
     field public static final java.lang.String ENABLED_INPUT_METHODS = "enabled_input_methods";
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index fddb429..3d36ebf 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -31,6 +31,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -135,6 +136,8 @@
             runToUri(false);
         } else if (op.equals("to-intent-uri")) {
             runToUri(true);
+        } else if (op.equals("switch-profile")) {
+            runSwitchUser();
         } else {
             throw new IllegalArgumentException("Unknown command: " + op);
         }
@@ -531,7 +534,8 @@
         Intent intent = makeIntent();
         IntentReceiver receiver = new IntentReceiver();
         System.out.println("Broadcasting: " + intent);
-        mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, null, true, false);
+        mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, null, true, false,
+                Binder.getOrigCallingUser());
         receiver.waitForFinish();
     }
 
@@ -722,6 +726,14 @@
         mAm.setDebugApp(null, false, true);
     }
 
+    private void runSwitchUser() throws Exception {
+        if (android.os.Process.myUid() != 0) {
+            throw new RuntimeException("switchuser can only be run as root");
+        }
+        String user = nextArgRequired();
+        mAm.switchUser(Integer.parseInt(user));
+    }
+
     class MyActivityController extends IActivityController.Stub {
         final String mGdbPort;
 
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index dd92bbe..203d180 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -148,6 +148,48 @@
     return delete_dir_contents(pkgdir, 1, NULL);
 }
 
+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];
+    DIR *d;
+    struct dirent *de;
+    struct stat s;
+    uid_t uid;
+
+    if (create_persona_path(src_data_dir, src_persona)) {
+        return -1;
+    }
+
+    d = opendir(src_data_dir);
+    if (d != NULL) {
+        while ((de = readdir(d))) {
+            const char *name = de->d_name;
+
+            if (de->d_type == DT_DIR) {
+                int subfd;
+                    /* always skip "." and ".." */
+                if (name[0] == '.') {
+                    if (name[1] == 0) continue;
+                    if ((name[1] == '.') && (name[2] == 0)) continue;
+                }
+                /* Create the full path to the package's data dir */
+                create_pkg_path(pkg_path, name, PKG_DIR_POSTFIX, src_persona);
+                /* Get the file stat */
+                if (stat(pkg_path, &s) < 0) continue;
+                /* Get the uid of the package */
+                ALOGI("Adding datadir for uid = %d\n", s.st_uid);
+                uid = (uid_t) s.st_uid % PER_USER_RANGE;
+                /* Create the directory for the target */
+                make_user_data(name, uid + target_persona * PER_USER_RANGE,
+                               target_persona);
+            }
+        }
+        closedir(d);
+    }
+    return 0;
+}
+
 int delete_cache(const char *pkgname)
 {
     char cachedir[PKG_PATH_MAX];
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index 569b491..7f94a96 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -107,6 +107,11 @@
     return delete_persona(atoi(arg[0])); /* userid */
 }
 
+static int do_clone_user_data(char **arg, char reply[REPLY_MAX])
+{
+    return clone_persona_data(atoi(arg[0]), atoi(arg[1]), atoi(arg[2]));
+}
+
 static int do_movefiles(char **arg, char reply[REPLY_MAX])
 {
     return movefiles();
@@ -146,6 +151,7 @@
     { "unlinklib",            1, do_unlinklib },
     { "mkuserdata",           3, do_mk_user_data },
     { "rmuser",               1, do_rm_user },
+    { "cloneuserdata",        3, do_clone_user_data },
 };
 
 static int readx(int s, void *_buf, int count)
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 173cabf..78342bb 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -72,6 +72,9 @@
 #define PKG_NAME_MAX  128   /* largest allowed package name */
 #define PKG_PATH_MAX  256   /* max size of any path we use */
 
+#define PER_USER_RANGE ((uid_t)100000)   /* range of uids per user
+                                            uid = persona * PER_USER_RANGE + appid */
+
 /* data structures */
 
 typedef struct {
@@ -143,6 +146,7 @@
 int delete_user_data(const char *pkgname, uid_t persona);
 int make_user_data(const char *pkgname, uid_t uid, uid_t persona);
 int delete_persona(uid_t persona);
+int clone_persona_data(uid_t src_persona, uid_t target_persona, int copy);
 int delete_cache(const char *pkgname);
 int move_dex(const char *src, const char *dst);
 int rm_dex(const char *path);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index c0ba543..f457842 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -16,8 +16,6 @@
 
 package com.android.commands.pm;
 
-import com.android.internal.content.PackageHelper;
-
 import android.app.ActivityManagerNative;
 import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
@@ -33,14 +31,17 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
+import android.content.pm.UserInfo;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.net.Uri;
-import android.os.Parcel;
+import android.os.Binder;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 
+import com.android.internal.content.PackageHelper;
+
 import java.io.File;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
@@ -135,13 +136,18 @@
             return;
         }
 
-        if ("createUser".equals(op)) {
-            runCreateUser();
+        if ("create-profile".equals(op)) {
+            runCreateProfile();
             return;
         }
 
-        if ("removeUser".equals(op)) {
-            runRemoveUser();
+        if ("remove-profile".equals(op)) {
+            runRemoveProfile();
+            return;
+        }
+
+        if ("list-profiles".equals(op)) {
+            runListProfiles();
             return;
         }
 
@@ -829,10 +835,10 @@
         }
     }
 
-    public void runCreateUser() {
+    public void runCreateProfile() {
         // Need to be run as root
         if (Process.myUid() != ROOT_UID) {
-            System.err.println("Error: createUser must be run as root");
+            System.err.println("Error: create-profile must be run as root");
             return;
         }
         String name;
@@ -845,7 +851,7 @@
         name = arg;
         try {
             if (mPm.createUser(name, 0) == null) {
-                System.err.println("Error: couldn't create user.");
+                System.err.println("Error: couldn't create profile.");
                 showUsage();
             }
         } catch (RemoteException e) {
@@ -855,10 +861,10 @@
 
     }
 
-    public void runRemoveUser() {
+    public void runRemoveProfile() {
         // Need to be run as root
         if (Process.myUid() != ROOT_UID) {
-            System.err.println("Error: removeUser must be run as root");
+            System.err.println("Error: remove-profile must be run as root");
             return;
         }
         int userId;
@@ -877,7 +883,7 @@
         }
         try {
             if (!mPm.removeUser(userId)) {
-                System.err.println("Error: couldn't remove user.");
+                System.err.println("Error: couldn't remove profile.");
                 showUsage();
             }
         } catch (RemoteException e) {
@@ -886,6 +892,27 @@
         }
     }
 
+    public void runListProfiles() {
+        // Need to be run as root
+        if (Process.myUid() != ROOT_UID) {
+            System.err.println("Error: list-profiles must be run as root");
+            return;
+        }
+        try {
+            List<UserInfo> users = mPm.getUsers();
+            if (users == null) {
+                System.err.println("Error: couldn't get users");
+            } else {
+                System.out.println("Users:");
+                for (int i = 0; i < users.size(); i++) {
+                    System.out.println("\t" + users.get(i).toString());
+                }
+            }
+        } catch (RemoteException e) {
+            System.err.println(e.toString());
+            System.err.println(PM_NOT_RUNNING_ERR);
+        }
+    }
     class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
         boolean finished;
         boolean result;
@@ -966,7 +993,8 @@
 
         ClearDataObserver obs = new ClearDataObserver();
         try {
-            if (!ActivityManagerNative.getDefault().clearApplicationUserData(pkg, obs)) {
+            if (!ActivityManagerNative.getDefault().clearApplicationUserData(pkg, obs,
+                    Binder.getOrigCallingUser())) {
                 System.err.println("Failed");
             }
 
@@ -1132,8 +1160,8 @@
         System.err.println("       pm disable-user PACKAGE_OR_COMPONENT");
         System.err.println("       pm set-install-location [0/auto] [1/internal] [2/external]");
         System.err.println("       pm get-install-location");
-        System.err.println("       pm createUser USER_NAME");
-        System.err.println("       pm removeUser USER_ID");
+        System.err.println("       pm create-profile USER_NAME");
+        System.err.println("       pm remove-profile USER_ID");
         System.err.println("");
         System.err.println("pm list packages: prints all packages, optionally only");
         System.err.println("  those whose package name contains the text in FILTER.  Options:");
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 7a599e9..bee5880 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -28,6 +28,7 @@
 
 #include <SkImageEncoder.h>
 #include <SkBitmap.h>
+#include <SkData.h>
 #include <SkStream.h>
 
 using namespace android;
@@ -168,7 +169,9 @@
             SkDynamicMemoryWStream stream;
             SkImageEncoder::EncodeStream(&stream, b,
                     SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);
-            write(fd, stream.getStream(), stream.getOffset());
+            SkData* streamData = stream.copyToData();
+            write(fd, streamData->data(), streamData->size());
+            streamData->unref();
         } else {
             write(fd, &w, 4);
             write(fd, &h, 4);
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index 274a9d5..634e4d8 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -960,17 +960,17 @@
         if (anim instanceof ObjectAnimator) {
             ((ObjectAnimator) anim).setCurrentPlayTime(0);
         }
-        if (mListeners != null) {
-            anim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator anim) {
-                    currentAppearingAnimations.remove(child);
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator anim) {
+                currentAppearingAnimations.remove(child);
+                if (mListeners != null) {
                     for (TransitionListener listener : mListeners) {
                         listener.endTransition(LayoutTransition.this, parent, child, APPEARING);
                     }
                 }
-            });
-        }
+            }
+        });
         currentAppearingAnimations.put(child, anim);
         anim.start();
     }
@@ -998,17 +998,19 @@
         anim.setStartDelay(mDisappearingDelay);
         anim.setDuration(mDisappearingDuration);
         anim.setTarget(child);
-        if (mListeners != null) {
-            anim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator anim) {
-                    currentDisappearingAnimations.remove(child);
+        final float preAnimAlpha = child.getAlpha();
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator anim) {
+                currentDisappearingAnimations.remove(child);
+                child.setAlpha(preAnimAlpha);
+                if (mListeners != null) {
                     for (TransitionListener listener : mListeners) {
                         listener.endTransition(LayoutTransition.this, parent, child, DISAPPEARING);
                     }
                 }
-            });
-        }
+            }
+        });
         if (anim instanceof ObjectAnimator) {
             ((ObjectAnimator) anim).setCurrentPlayTime(0);
         }
diff --git a/core/java/android/animation/TimeAnimator.java b/core/java/android/animation/TimeAnimator.java
index 0a96d59..a79f2a3 100644
--- a/core/java/android/animation/TimeAnimator.java
+++ b/core/java/android/animation/TimeAnimator.java
@@ -1,13 +1,11 @@
 package android.animation;
 
 /**
- * This class provides a simple callback mechanism to listeners that is synchronized with other
- * animators in the system. There is no duration, interpolation, or object value-setting
- * with this Animator. Instead, it is simply started and proceeds to send out events on every
- * animation frame to its TimeListener (if set), with information about this animator,
- * the total elapsed time, and the time since the last animation frame.
- *
- * @hide
+ * This class provides a simple callback mechanism to listeners that is synchronized with all
+ * other animators in the system. There is no duration, interpolation, or object value-setting
+ * with this Animator. Instead, it is simply started, after which it proceeds to send out events
+ * on every animation frame to its TimeListener (if set), with information about this animator,
+ * the total elapsed time, and the elapsed time since the previous animation frame.
  */
 public class TimeAnimator extends ValueAnimator {
 
@@ -59,10 +57,10 @@
      * Implementors of this interface can set themselves as update listeners
      * to a <code>TimeAnimator</code> instance to receive callbacks on every animation
      * frame to receive the total time since the animator started and the delta time
-     * since the last frame. The first time the listener is called, totalTime and
-     * deltaTime should both be zero.
-     *
-     * @hide
+     * since the last frame. The first time the listener is called,
+     * deltaTime will be zero. The same is true for totalTime, unless the animator was
+     * set to a specific {@link ValueAnimator#setCurrentPlayTime(long) currentPlayTime}
+     * prior to starting.
      */
     public static interface TimeListener {
         /**
@@ -70,7 +68,8 @@
          * along with information about the elapsed time.</p>
          *
          * @param animation The animator sending out the notification.
-         * @param totalTime The
+         * @param totalTime The total time elapsed since the animator started, in milliseconds.
+         * @param deltaTime The time elapsed since the previous frame, in milliseconds.
          */
         void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime);
 
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 9661b9e..d98d87b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -30,6 +30,7 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Point;
+import android.os.Binder;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.Parcel;
@@ -975,7 +976,7 @@
     public boolean clearApplicationUserData(String packageName, IPackageDataObserver observer) {
         try {
             return ActivityManagerNative.getDefault().clearApplicationUserData(packageName, 
-                    observer);
+                    observer, Binder.getOrigCallingUser());
         } catch (RemoteException e) {
             return false;
         }
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 7994d7c..d80902d 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -91,7 +91,7 @@
         try {
             getDefault().broadcastIntent(
                 null, intent, null, null, Activity.RESULT_OK, null, null,
-                null /*permission*/, false, true);
+                null /*permission*/, false, true, Binder.getOrigCallingUser());
         } catch (RemoteException ex) {
         }
     }
@@ -306,9 +306,10 @@
             String perm = data.readString();
             boolean serialized = data.readInt() != 0;
             boolean sticky = data.readInt() != 0;
+            int userId = data.readInt();
             int res = broadcastIntent(app, intent, resolvedType, resultTo,
                     resultCode, resultData, resultExtras, perm,
-                    serialized, sticky);
+                    serialized, sticky, userId);
             reply.writeNoException();
             reply.writeInt(res);
             return true;
@@ -320,7 +321,8 @@
             IBinder b = data.readStrongBinder();
             IApplicationThread app = b != null ? ApplicationThreadNative.asInterface(b) : null;
             Intent intent = Intent.CREATOR.createFromParcel(data);
-            unbroadcastIntent(app, intent);
+            int userId = data.readInt();
+            unbroadcastIntent(app, intent, userId);
             reply.writeNoException();
             return true;
         }
@@ -900,7 +902,8 @@
             String packageName = data.readString();
             IPackageDataObserver observer = IPackageDataObserver.Stub.asInterface(
                     data.readStrongBinder());
-            boolean res = clearApplicationUserData(packageName, observer);
+            int userId = data.readInt();
+            boolean res = clearApplicationUserData(packageName, observer, userId);
             reply.writeNoException();
             reply.writeInt(res ? 1 : 0);
             return true;
@@ -1819,7 +1822,7 @@
             Intent intent, String resolvedType,  IIntentReceiver resultTo,
             int resultCode, String resultData, Bundle map,
             String requiredPermission, boolean serialized,
-            boolean sticky) throws RemoteException
+            boolean sticky, int userId) throws RemoteException
     {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -1834,6 +1837,7 @@
         data.writeString(requiredPermission);
         data.writeInt(serialized ? 1 : 0);
         data.writeInt(sticky ? 1 : 0);
+        data.writeInt(userId);
         mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0);
         reply.readException();
         int res = reply.readInt();
@@ -1841,13 +1845,15 @@
         data.recycle();
         return res;
     }
-    public void unbroadcastIntent(IApplicationThread caller, Intent intent) throws RemoteException
+    public void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId)
+            throws RemoteException
     {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(caller != null ? caller.asBinder() : null);
         intent.writeToParcel(data, 0);
+        data.writeInt(userId);
         mRemote.transact(UNBROADCAST_INTENT_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
@@ -2651,12 +2657,13 @@
         return res;
     }
     public boolean clearApplicationUserData(final String packageName,
-            final IPackageDataObserver observer) throws RemoteException {        
+            final IPackageDataObserver observer, final int userId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeString(packageName);
         data.writeStrongBinder(observer.asBinder());
+        data.writeInt(userId);
         mRemote.transact(CLEAR_APP_DATA_TRANSACTION, data, reply, 0);
         reply.readException();
         boolean res = reply.readInt() != 0;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3c5f53a..e4cfc99 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -47,6 +47,7 @@
 import android.net.ProxyProperties;
 import android.opengl.GLUtils;
 import android.os.AsyncTask;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
@@ -60,6 +61,7 @@
 import android.os.ServiceManager;
 import android.os.StrictMode;
 import android.os.SystemClock;
+import android.os.UserId;
 import android.util.AndroidRuntimeException;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
@@ -132,6 +134,7 @@
     private static final boolean DEBUG_RESULTS = false;
     private static final boolean DEBUG_BACKUP = true;
     private static final boolean DEBUG_CONFIGURATION = false;
+    private static final boolean DEBUG_SERVICE = true;
     private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
     private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
     private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
@@ -635,6 +638,9 @@
             s.intent = intent;
             s.rebind = rebind;
 
+            if (DEBUG_SERVICE)
+                Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
+                        + Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
             queueOrSendMessage(H.BIND_SERVICE, s);
         }
 
@@ -1592,7 +1598,8 @@
         boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
         boolean securityViolation = includeCode && ai.uid != 0
                 && ai.uid != Process.SYSTEM_UID && (mBoundApplication != null
-                        ? ai.uid != mBoundApplication.appInfo.uid : true);
+                        ? !UserId.isSameApp(ai.uid, mBoundApplication.appInfo.uid)
+                        : true);
         if ((flags&(Context.CONTEXT_INCLUDE_CODE
                 |Context.CONTEXT_IGNORE_SECURITY))
                 == Context.CONTEXT_INCLUDE_CODE) {
@@ -2294,6 +2301,8 @@
 
     private void handleBindService(BindServiceData data) {
         Service s = mServices.get(data.token);
+        if (DEBUG_SERVICE)
+            Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
         if (s != null) {
             try {
                 data.intent.setExtrasClassLoader(s.getClassLoader());
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 180a442..fee2beb 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1177,13 +1177,14 @@
      */
     @Override
     public List<UserInfo> getUsers() {
-        // TODO:
-        // Dummy code, always returns just the primary user
-        ArrayList<UserInfo> users = new ArrayList<UserInfo>();
-        UserInfo primary = new UserInfo(0, "Root!",
-                UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
-        users.add(primary);
-        return users;
+        try {
+            return mPM.getUsers();
+        } catch (RemoteException re) {
+            ArrayList<UserInfo> users = new ArrayList<UserInfo>();
+            UserInfo primary = new UserInfo(0, "Root!", UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
+            users.add(primary);
+            return users;
+        }
     }
 
     /**
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 2bf1fb7..db5113e 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -75,6 +75,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserId;
 import android.os.Vibrator;
 import android.os.storage.StorageManager;
 import android.telephony.TelephonyManager;
@@ -896,7 +897,8 @@
             intent.setAllowFds(false);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, null, false, false);
+                Activity.RESULT_OK, null, null, null, false, false,
+                Binder.getOrigCallingUser());
         } catch (RemoteException e) {
         }
     }
@@ -908,7 +910,8 @@
             intent.setAllowFds(false);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, receiverPermission, false, false);
+                Activity.RESULT_OK, null, null, receiverPermission, false, false,
+                Binder.getOrigCallingUser());
         } catch (RemoteException e) {
         }
     }
@@ -921,7 +924,8 @@
             intent.setAllowFds(false);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, receiverPermission, true, false);
+                Activity.RESULT_OK, null, null, receiverPermission, true, false,
+                Binder.getOrigCallingUser());
         } catch (RemoteException e) {
         }
     }
@@ -954,7 +958,7 @@
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, receiverPermission,
-                true, false);
+                true, false, Binder.getOrigCallingUser());
         } catch (RemoteException e) {
         }
     }
@@ -966,7 +970,8 @@
             intent.setAllowFds(false);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, null, false, true);
+                Activity.RESULT_OK, null, null, null, false, true,
+                Binder.getOrigCallingUser());
         } catch (RemoteException e) {
         }
     }
@@ -999,7 +1004,7 @@
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, null,
-                true, true);
+                true, true, Binder.getOrigCallingUser());
         } catch (RemoteException e) {
         }
     }
@@ -1014,7 +1019,7 @@
         try {
             intent.setAllowFds(false);
             ActivityManagerNative.getDefault().unbroadcastIntent(
-                mMainThread.getApplicationThread(), intent);
+                    mMainThread.getApplicationThread(), intent, Binder.getOrigCallingUser());
         } catch (RemoteException e) {
         }
     }
@@ -1215,8 +1220,7 @@
 
         int pid = Binder.getCallingPid();
         if (pid != Process.myPid()) {
-            return checkPermission(permission, pid,
-                    Binder.getCallingUid());
+            return checkPermission(permission, pid, Binder.getCallingUid());
         }
         return PackageManager.PERMISSION_DENIED;
     }
@@ -1384,7 +1388,8 @@
             Uri uri, int modeFlags, String message) {
         enforceForUri(
                 modeFlags, checkCallingUriPermission(uri, modeFlags),
-                false, Binder.getCallingUid(), uri, message);
+                false,
+                Binder.getCallingUid(), uri, message);
     }
 
     public void enforceCallingOrSelfUriPermission(
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 5222d37..39817ac 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -114,8 +114,8 @@
     public int broadcastIntent(IApplicationThread caller, Intent intent,
             String resolvedType, IIntentReceiver resultTo, int resultCode,
             String resultData, Bundle map, String requiredPermission,
-            boolean serialized, boolean sticky) throws RemoteException;
-    public void unbroadcastIntent(IApplicationThread caller, Intent intent) throws RemoteException;
+            boolean serialized, boolean sticky, int userId) throws RemoteException;
+    public void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) throws RemoteException;
     /* oneway */
     public void finishReceiver(IBinder who, int resultCode, String resultData, Bundle map, boolean abortBroadcast) throws RemoteException;
     public void attachApplication(IApplicationThread app) throws RemoteException;
@@ -209,7 +209,7 @@
             int flags) throws RemoteException;
     public void cancelIntentSender(IIntentSender sender) throws RemoteException;
     public boolean clearApplicationUserData(final String packageName,
-            final IPackageDataObserver observer) throws RemoteException;
+            final IPackageDataObserver observer, int userId) throws RemoteException;
     public String getPackageForIntentSender(IIntentSender sender) throws RemoteException;
     
     public void setProcessLimit(int max) throws RemoteException;
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 0c6baeb..fcbcd81 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -36,6 +36,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.StrictMode;
+import android.os.UserId;
 import android.util.AndroidRuntimeException;
 import android.util.Slog;
 import android.view.CompatibilityInfoHolder;
@@ -67,6 +68,8 @@
  */
 public final class LoadedApk {
 
+    private static final String TAG = "LoadedApk";
+
     private final ActivityThread mActivityThread;
     private final ApplicationInfo mApplicationInfo;
     final String mPackageName;
@@ -113,8 +116,13 @@
         mApplicationInfo = aInfo;
         mPackageName = aInfo.packageName;
         mAppDir = aInfo.sourceDir;
-        mResDir = aInfo.uid == Process.myUid() ? aInfo.sourceDir
+        final int myUid = Process.myUid();
+        mResDir = aInfo.uid == myUid ? aInfo.sourceDir
                 : aInfo.publicSourceDir;
+        if (!UserId.isSameUser(aInfo.uid, myUid)) {
+            aInfo.dataDir = PackageManager.getDataDirForUser(UserId.getUserId(myUid),
+                    mPackageName);
+        }
         mSharedLibraries = aInfo.sharedLibraryFiles;
         mDataDir = aInfo.dataDir;
         mDataDirFile = mDataDir != null ? new File(mDataDir) : null;
diff --git a/core/java/android/content/CancelationSignal.java b/core/java/android/content/CancellationSignal.java
similarity index 69%
rename from core/java/android/content/CancelationSignal.java
rename to core/java/android/content/CancellationSignal.java
index 58cf59d..2dbbe54 100644
--- a/core/java/android/content/CancelationSignal.java
+++ b/core/java/android/content/CancellationSignal.java
@@ -21,15 +21,15 @@
 /**
  * Provides the ability to cancel an operation in progress.
  */
-public final class CancelationSignal {
+public final class CancellationSignal {
     private boolean mIsCanceled;
     private OnCancelListener mOnCancelListener;
-    private ICancelationSignal mRemote;
+    private ICancellationSignal mRemote;
 
     /**
-     * Creates a cancelation signal, initially not canceled.
+     * Creates a cancellation signal, initially not canceled.
      */
-    public CancelationSignal() {
+    public CancellationSignal() {
     }
 
     /**
@@ -55,7 +55,7 @@
     }
 
     /**
-     * Cancels the operation and signals the cancelation listener.
+     * Cancels the operation and signals the cancellation listener.
      * If the operation has not yet started, then it will be canceled as soon as it does.
      */
     public void cancel() {
@@ -76,17 +76,23 @@
     }
 
     /**
-     * Sets the cancelation listener to be called when canceled.
-     * If {@link CancelationSignal#cancel} has already been called, then the provided
+     * Sets the cancellation listener to be called when canceled.
+     *
+     * This method is intended to be used by the recipient of a cancellation signal
+     * such as a database or a content provider to handle cancellation requests
+     * while performing a long-running operation.  This method is not intended to be
+     * used by applications themselves.
+     *
+     * If {@link CancellationSignal#cancel} has already been called, then the provided
      * listener is invoked immediately.
      *
-     * The listener is called while holding the cancelation signal's lock which is
+     * The listener is called while holding the cancellation signal's lock which is
      * also held while registering or unregistering the listener.  Because of the lock,
      * it is not possible for the listener to run after it has been unregistered.
-     * This design choice makes it easier for clients of {@link CancelationSignal} to
+     * This design choice makes it easier for clients of {@link CancellationSignal} to
      * prevent race conditions related to listener registration and unregistration.
      *
-     * @param listener The cancelation listener, or null to remove the current listener.
+     * @param listener The cancellation listener, or null to remove the current listener.
      */
     public void setOnCancelListener(OnCancelListener listener) {
         synchronized (this) {
@@ -104,7 +110,7 @@
      *
      * @hide
      */
-    public void setRemote(ICancelationSignal remote) {
+    public void setRemote(ICancellationSignal remote) {
         synchronized (this) {
             mRemote = remote;
             if (mIsCanceled && remote != null) {
@@ -118,47 +124,47 @@
 
     /**
      * Creates a transport that can be returned back to the caller of
-     * a Binder function and subsequently used to dispatch a cancelation signal.
+     * a Binder function and subsequently used to dispatch a cancellation signal.
      *
-     * @return The new cancelation signal transport.
+     * @return The new cancellation signal transport.
      *
      * @hide
      */
-    public static ICancelationSignal createTransport() {
+    public static ICancellationSignal createTransport() {
         return new Transport();
     }
 
     /**
-     * Given a locally created transport, returns its associated cancelation signal.
+     * Given a locally created transport, returns its associated cancellation signal.
      *
      * @param transport The locally created transport, or null if none.
-     * @return The associated cancelation signal, or null if none.
+     * @return The associated cancellation signal, or null if none.
      *
      * @hide
      */
-    public static CancelationSignal fromTransport(ICancelationSignal transport) {
+    public static CancellationSignal fromTransport(ICancellationSignal transport) {
         if (transport instanceof Transport) {
-            return ((Transport)transport).mCancelationSignal;
+            return ((Transport)transport).mCancellationSignal;
         }
         return null;
     }
 
     /**
-     * Listens for cancelation.
+     * Listens for cancellation.
      */
     public interface OnCancelListener {
         /**
-         * Called when {@link CancelationSignal#cancel} is invoked.
+         * Called when {@link CancellationSignal#cancel} is invoked.
          */
         void onCancel();
     }
 
-    private static final class Transport extends ICancelationSignal.Stub {
-        final CancelationSignal mCancelationSignal = new CancelationSignal();
+    private static final class Transport extends ICancellationSignal.Stub {
+        final CancellationSignal mCancellationSignal = new CancellationSignal();
 
         @Override
         public void cancel() throws RemoteException {
-            mCancelationSignal.cancel();
+            mCancellationSignal.cancel();
         }
     }
 }
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index adbeb6a..12e3ccf 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -178,10 +178,10 @@
         @Override
         public Cursor query(Uri uri, String[] projection,
                 String selection, String[] selectionArgs, String sortOrder,
-                ICancelationSignal cancelationSignal) {
+                ICancellationSignal cancellationSignal) {
             enforceReadPermission(uri);
             return ContentProvider.this.query(uri, projection, selection, selectionArgs, sortOrder,
-                    CancelationSignal.fromTransport(cancelationSignal));
+                    CancellationSignal.fromTransport(cancellationSignal));
         }
 
         @Override
@@ -263,8 +263,8 @@
         }
 
         @Override
-        public ICancelationSignal createCancelationSignal() throws RemoteException {
-            return CancelationSignal.createTransport();
+        public ICancellationSignal createCancellationSignal() throws RemoteException {
+            return CancellationSignal.createTransport();
         }
 
         private void enforceReadPermission(Uri uri) {
@@ -557,7 +557,7 @@
             String selection, String[] selectionArgs, String sortOrder);
 
     /**
-     * Implement this to handle query requests from clients with support for cancelation.
+     * Implement this to handle query requests from clients with support for cancellation.
      * This method can be called from multiple threads, as described in
      * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
      * and Threads</a>.
@@ -597,9 +597,9 @@
         return c;</pre>
      * <p>
      * If you implement this method then you must also implement the version of
-     * {@link #query(Uri, String[], String, String[], String)} that does not take a cancelation
-     * provider to ensure correct operation on older versions of the Android Framework in
-     * which the cancelation signal overload was not available.
+     * {@link #query(Uri, String[], String, String[], String)} that does not take a cancellation
+     * signal to ensure correct operation on older versions of the Android Framework in
+     * which the cancellation signal overload was not available.
      *
      * @param uri The URI to query. This will be the full URI sent by the client;
      *      if the client is requesting a specific record, the URI will end in a record number
@@ -614,14 +614,14 @@
      *      The values will be bound as Strings.
      * @param sortOrder How the rows in the cursor should be sorted.
      *      If null then the provider is free to define the sort order.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * If the operation is canceled, then {@link OperationCanceledException} will be thrown
      * when the query is executed.
      * @return a Cursor or null.
      */
     public Cursor query(Uri uri, String[] projection,
             String selection, String[] selectionArgs, String sortOrder,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         return query(uri, projection, selection, selectionArgs, sortOrder);
     }
 
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 9a1fa65..3ac5e07 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -52,15 +52,15 @@
 
     /** See {@link ContentProvider#query ContentProvider.query} */
     public Cursor query(Uri url, String[] projection, String selection,
-            String[] selectionArgs, String sortOrder, CancelationSignal cancelationSignal)
+            String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal)
                     throws RemoteException {
-        ICancelationSignal remoteCancelationSignal = null;
-        if (cancelationSignal != null) {
-            remoteCancelationSignal = mContentProvider.createCancelationSignal();
-            cancelationSignal.setRemote(remoteCancelationSignal);
+        ICancellationSignal remoteCancellationSignal = null;
+        if (cancellationSignal != null) {
+            remoteCancellationSignal = mContentProvider.createCancellationSignal();
+            cancellationSignal.setRemote(remoteCancellationSignal);
         }
         return mContentProvider.query(url, projection, selection,  selectionArgs, sortOrder,
-                remoteCancelationSignal);
+                remoteCancellationSignal);
     }
 
     /** See {@link ContentProvider#getType ContentProvider.getType} */
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index e0e277a..eb83dbc 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -105,11 +105,11 @@
                     String sortOrder = data.readString();
                     IContentObserver observer = IContentObserver.Stub.asInterface(
                             data.readStrongBinder());
-                    ICancelationSignal cancelationSignal = ICancelationSignal.Stub.asInterface(
+                    ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
                             data.readStrongBinder());
 
                     Cursor cursor = query(url, projection, selection, selectionArgs, sortOrder,
-                            cancelationSignal);
+                            cancellationSignal);
                     if (cursor != null) {
                         CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor(
                                 cursor, observer, getProviderName());
@@ -300,9 +300,9 @@
                 {
                     data.enforceInterface(IContentProvider.descriptor);
 
-                    ICancelationSignal cancelationSignal = createCancelationSignal();
+                    ICancellationSignal cancellationSignal = createCancellationSignal();
                     reply.writeNoException();
-                    reply.writeStrongBinder(cancelationSignal.asBinder());
+                    reply.writeStrongBinder(cancellationSignal.asBinder());
                     return true;
                 }
             }
@@ -334,7 +334,7 @@
     }
 
     public Cursor query(Uri url, String[] projection, String selection,
-            String[] selectionArgs, String sortOrder, ICancelationSignal cancelationSignal)
+            String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal)
                     throws RemoteException {
         BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
         Parcel data = Parcel.obtain();
@@ -363,7 +363,7 @@
             }
             data.writeString(sortOrder);
             data.writeStrongBinder(adaptor.getObserver().asBinder());
-            data.writeStrongBinder(cancelationSignal != null ? cancelationSignal.asBinder() : null);
+            data.writeStrongBinder(cancellationSignal != null ? cancellationSignal.asBinder() : null);
 
             mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
 
@@ -632,7 +632,7 @@
         }
     }
 
-    public ICancelationSignal createCancelationSignal() throws RemoteException {
+    public ICancellationSignal createCancellationSignal() throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         try {
@@ -642,9 +642,9 @@
                     data, reply, 0);
 
             DatabaseUtils.readExceptionFromParcel(reply);
-            ICancelationSignal cancelationSignal = ICancelationSignal.Stub.asInterface(
+            ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
                     reply.readStrongBinder());
-            return cancelationSignal;
+            return cancellationSignal;
         } finally {
             data.recycle();
             reply.recycle();
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index e79475a..96a65da 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -335,7 +335,7 @@
      * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
      *         clause (excluding the ORDER BY itself). Passing null will use the
      *         default sort order, which may be unordered.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * If the operation is canceled, then {@link OperationCanceledException} will be thrown
      * when the query is executed.
      * @return A Cursor object, which is positioned before the first entry, or null
@@ -343,7 +343,7 @@
      */
     public final Cursor query(final Uri uri, String[] projection,
             String selection, String[] selectionArgs, String sortOrder,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         IContentProvider provider = acquireProvider(uri);
         if (provider == null) {
             return null;
@@ -351,14 +351,14 @@
         try {
             long startTime = SystemClock.uptimeMillis();
 
-            ICancelationSignal remoteCancelationSignal = null;
-            if (cancelationSignal != null) {
-                cancelationSignal.throwIfCanceled();
-                remoteCancelationSignal = provider.createCancelationSignal();
-                cancelationSignal.setRemote(remoteCancelationSignal);
+            ICancellationSignal remoteCancellationSignal = null;
+            if (cancellationSignal != null) {
+                cancellationSignal.throwIfCanceled();
+                remoteCancellationSignal = provider.createCancellationSignal();
+                cancellationSignal.setRemote(remoteCancellationSignal);
             }
             Cursor qCursor = provider.query(uri, projection,
-                    selection, selectionArgs, sortOrder, remoteCancelationSignal);
+                    selection, selectionArgs, sortOrder, remoteCancellationSignal);
             if (qCursor == null) {
                 releaseProvider(provider);
                 return null;
diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java
index e58fad7..aed3728 100644
--- a/core/java/android/content/CursorLoader.java
+++ b/core/java/android/content/CursorLoader.java
@@ -48,7 +48,7 @@
     String mSortOrder;
 
     Cursor mCursor;
-    CancelationSignal mCancelationSignal;
+    CancellationSignal mCancellationSignal;
 
     /* Runs on a worker thread */
     @Override
@@ -57,11 +57,11 @@
             if (isLoadInBackgroundCanceled()) {
                 throw new OperationCanceledException();
             }
-            mCancelationSignal = new CancelationSignal();
+            mCancellationSignal = new CancellationSignal();
         }
         try {
             Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
-                    mSelectionArgs, mSortOrder, mCancelationSignal);
+                    mSelectionArgs, mSortOrder, mCancellationSignal);
             if (cursor != null) {
                 // Ensure the cursor window is filled
                 cursor.getCount();
@@ -70,7 +70,7 @@
             return cursor;
         } finally {
             synchronized (this) {
-                mCancelationSignal = null;
+                mCancellationSignal = null;
             }
         }
     }
@@ -80,8 +80,8 @@
         super.cancelLoadInBackground();
 
         synchronized (this) {
-            if (mCancelationSignal != null) {
-                mCancelationSignal.cancel();
+            if (mCancellationSignal != null) {
+                mCancellationSignal.cancel();
             }
         }
     }
diff --git a/core/java/android/content/ICancelationSignal.aidl b/core/java/android/content/ICancellationSignal.aidl
similarity index 95%
rename from core/java/android/content/ICancelationSignal.aidl
rename to core/java/android/content/ICancellationSignal.aidl
index 3f5a24d..cf1c5d3 100644
--- a/core/java/android/content/ICancelationSignal.aidl
+++ b/core/java/android/content/ICancellationSignal.aidl
@@ -19,6 +19,6 @@
 /**
  * @hide
  */
-interface ICancelationSignal {
+interface ICancellationSignal {
     oneway void cancel();
 }
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index f52157f..16478b7 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -34,7 +34,7 @@
  */
 public interface IContentProvider extends IInterface {
     public Cursor query(Uri url, String[] projection, String selection,
-            String[] selectionArgs, String sortOrder, ICancelationSignal cancelationSignal)
+            String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal)
                     throws RemoteException;
     public String getType(Uri url) throws RemoteException;
     public Uri insert(Uri url, ContentValues initialValues)
@@ -51,7 +51,7 @@
     public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
             throws RemoteException, OperationApplicationException;
     public Bundle call(String method, String arg, Bundle extras) throws RemoteException;
-    public ICancelationSignal createCancelationSignal() throws RemoteException;
+    public ICancellationSignal createCancellationSignal() throws RemoteException;
 
     // Data interchange.
     public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException;
diff --git a/core/java/android/content/OperationCanceledException.java b/core/java/android/content/OperationCanceledException.java
index 24afcfa..d783a07 100644
--- a/core/java/android/content/OperationCanceledException.java
+++ b/core/java/android/content/OperationCanceledException.java
@@ -19,7 +19,7 @@
 /**
  * An exception type that is thrown when an operation in progress is canceled.
  *
- * @see CancelationSignal
+ * @see CancellationSignal
  */
 public class OperationCanceledException extends RuntimeException {
     public OperationCanceledException() {
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index decb974..bb35c29 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -364,4 +364,6 @@
     VerifierDeviceIdentity getVerifierDeviceIdentity();
 
     boolean isFirstBoot();
+
+    List<UserInfo> getUsers();
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8541748d..26a9181 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -28,6 +28,7 @@
 import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.Environment;
 import android.util.AndroidException;
 import android.util.DisplayMetrics;
 
@@ -753,13 +754,6 @@
     public static final int VERIFICATION_REJECT = -1;
 
     /**
-     * Range of IDs allocated for a user.
-     *
-     * @hide
-     */
-    public static final int PER_USER_RANGE = 100000;
-
-    /**
      * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device's
      * audio pipeline is low-latency, more suitable for audio applications sensitive to delays or
      * lag in sound input or output.
@@ -2615,39 +2609,6 @@
     public abstract void updateUserFlags(int id, int flags);
 
     /**
-     * Checks to see if the user id is the same for the two uids, i.e., they belong to the same
-     * user.
-     * @hide
-     */
-    public static boolean isSameUser(int uid1, int uid2) {
-        return getUserId(uid1) == getUserId(uid2);
-    }
-
-    /**
-     * Returns the user id for a given uid.
-     * @hide
-     */
-    public static int getUserId(int uid) {
-        return uid / PER_USER_RANGE;
-    }
-
-    /**
-     * Returns the uid that is composed from the userId and the appId.
-     * @hide
-     */
-    public static int getUid(int userId, int appId) {
-        return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
-    }
-
-    /**
-     * Returns the app id (or base uid) for a given uid, stripping out the user id from it.
-     * @hide
-     */
-    public static int getAppId(int uid) {
-        return uid % PER_USER_RANGE;
-    }
-
-    /**
      * Returns the device identity that verifiers can use to associate their
      * scheme to a particular device. This should not be used by anything other
      * than a package verifier.
@@ -2656,4 +2617,17 @@
      * @hide
      */
     public abstract VerifierDeviceIdentity getVerifierDeviceIdentity();
+
+    /**
+     * Returns the data directory for a particular user and package, given the uid of the package.
+     * @param uid uid of the package, including the userId and appId
+     * @param packageName name of the package
+     * @return the user-specific data directory for the package
+     * @hide
+     */
+    public static String getDataDirForUser(int userId, String packageName) {
+        // TODO: This should be shared with Installer's knowledge of user directory
+        return Environment.getDataDirectory().toString() + "/user/" + userId
+                + "/" + packageName;
+    }
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e593d5b..faee873 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -24,18 +24,17 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.PatternMatcher;
+import android.os.UserId;
 import android.util.AttributeSet;
 import android.util.Base64;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Slog;
 import android.util.TypedValue;
-import com.android.internal.util.XmlUtils;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.BufferedInputStream;
 import java.io.File;
@@ -59,6 +58,11 @@
 import java.util.jar.JarFile;
 import java.util.jar.Manifest;
 
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 /**
  * Package archive parsing
  *
@@ -209,6 +213,8 @@
     public static PackageInfo generatePackageInfo(PackageParser.Package p,
             int gids[], int flags, long firstInstallTime, long lastUpdateTime) {
 
+        final int userId = Binder.getOrigCallingUser();
+
         PackageInfo pi = new PackageInfo();
         pi.packageName = p.packageName;
         pi.versionCode = p.mVersionCode;
@@ -250,7 +256,8 @@
                     final Activity activity = p.activities.get(i);
                     if (activity.info.enabled
                         || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags);
+                        pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags,
+                                userId);
                     }
                 }
             }
@@ -271,7 +278,7 @@
                     final Activity activity = p.receivers.get(i);
                     if (activity.info.enabled
                         || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags);
+                        pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags, userId);
                     }
                 }
             }
@@ -292,7 +299,7 @@
                     final Service service = p.services.get(i);
                     if (service.info.enabled
                         || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.services[j++] = generateServiceInfo(p.services.get(i), flags);
+                        pi.services[j++] = generateServiceInfo(p.services.get(i), flags, userId);
                     }
                 }
             }
@@ -313,7 +320,7 @@
                     final Provider provider = p.providers.get(i);
                     if (provider.info.enabled
                         || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags);
+                        pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags, userId);
                     }
                 }
             }
@@ -3241,8 +3248,12 @@
     }
 
     public static ApplicationInfo generateApplicationInfo(Package p, int flags) {
+        return generateApplicationInfo(p, flags, UserId.getUserId(Binder.getCallingUid()));
+    }
+
+    public static ApplicationInfo generateApplicationInfo(Package p, int flags, int userId) {
         if (p == null) return null;
-        if (!copyNeeded(flags, p, null)) {
+        if (!copyNeeded(flags, p, null) && userId == 0) {
             // CompatibilityMode is global state. It's safe to modify the instance
             // of the package.
             if (!sCompatibilityModeEnabled) {
@@ -3258,6 +3269,10 @@
 
         // 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.dataDir = PackageManager.getDataDirForUser(userId, ai.packageName);
+        }
         if ((flags & PackageManager.GET_META_DATA) != 0) {
             ai.metaData = p.mAppMetaData;
         }
@@ -3325,16 +3340,15 @@
         }
     }
 
-    public static final ActivityInfo generateActivityInfo(Activity a,
-            int flags) {
+    public static final ActivityInfo generateActivityInfo(Activity a, int flags, int userId) {
         if (a == null) return null;
-        if (!copyNeeded(flags, a.owner, a.metaData)) {
+        if (!copyNeeded(flags, a.owner, a.metaData) && userId == 0) {
             return a.info;
         }
         // Make shallow copies so we can store the metadata safely
         ActivityInfo ai = new ActivityInfo(a.info);
         ai.metaData = a.metaData;
-        ai.applicationInfo = generateApplicationInfo(a.owner, flags);
+        ai.applicationInfo = generateApplicationInfo(a.owner, flags, userId);
         return ai;
     }
 
@@ -3359,15 +3373,15 @@
         }
     }
 
-    public static final ServiceInfo generateServiceInfo(Service s, int flags) {
+    public static final ServiceInfo generateServiceInfo(Service s, int flags, int userId) {
         if (s == null) return null;
-        if (!copyNeeded(flags, s.owner, s.metaData)) {
+        if (!copyNeeded(flags, s.owner, s.metaData) && userId == 0) {
             return s.info;
         }
         // Make shallow copies so we can store the metadata safely
         ServiceInfo si = new ServiceInfo(s.info);
         si.metaData = s.metaData;
-        si.applicationInfo = generateApplicationInfo(s.owner, flags);
+        si.applicationInfo = generateApplicationInfo(s.owner, flags, userId);
         return si;
     }
 
@@ -3400,12 +3414,12 @@
         }
     }
 
-    public static final ProviderInfo generateProviderInfo(Provider p,
-            int flags) {
+    public static final ProviderInfo generateProviderInfo(Provider p, int flags, int userId) {
         if (p == null) return null;
         if (!copyNeeded(flags, p.owner, p.metaData)
                 && ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0
-                        || p.info.uriPermissionPatterns == null)) {
+                        || p.info.uriPermissionPatterns == null)
+                && userId == 0) {
             return p.info;
         }
         // Make shallow copies so we can store the metadata safely
@@ -3414,7 +3428,7 @@
         if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
             pi.uriPermissionPatterns = null;
         }
-        pi.applicationInfo = generateApplicationInfo(p.owner, flags);
+        pi.applicationInfo = generateApplicationInfo(p.owner, flags, userId);
         return pi;
     }
 
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 710bd53..b5cef81 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -19,7 +19,7 @@
 import dalvik.system.BlockGuard;
 import dalvik.system.CloseGuard;
 
-import android.content.CancelationSignal;
+import android.content.CancellationSignal;
 import android.content.OperationCanceledException;
 import android.database.Cursor;
 import android.database.CursorWindow;
@@ -84,7 +84,7 @@
  *
  * @hide
  */
-public final class SQLiteConnection implements CancelationSignal.OnCancelListener {
+public final class SQLiteConnection implements CancellationSignal.OnCancelListener {
     private static final String TAG = "SQLiteConnection";
     private static final boolean DEBUG = false;
 
@@ -110,11 +110,11 @@
 
     private boolean mOnlyAllowReadOnlyOperations;
 
-    // The number of times attachCancelationSignal has been called.
+    // The number of times attachCancellationSignal has been called.
     // Because SQLite statement execution can be re-entrant, we keep track of how many
-    // times we have attempted to attach a cancelation signal to the connection so that
+    // times we have attempted to attach a cancellation signal to the connection so that
     // we can ensure that we detach the signal at the right time.
-    private int mCancelationSignalAttachCount;
+    private int mCancellationSignalAttachCount;
 
     private static native int nativeOpen(String path, int openFlags, String label,
             boolean enableTrace, boolean enableProfile);
@@ -355,14 +355,14 @@
      *
      * @param sql The SQL statement to execute.
      * @param bindArgs The arguments to bind, or null if none.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      *
      * @throws SQLiteException if an error occurs, such as a syntax error
      * or invalid number of bind arguments.
      * @throws OperationCanceledException if the operation was canceled.
      */
     public void execute(String sql, Object[] bindArgs,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         if (sql == null) {
             throw new IllegalArgumentException("sql must not be null.");
         }
@@ -374,11 +374,11 @@
                 throwIfStatementForbidden(statement);
                 bindArguments(statement, bindArgs);
                 applyBlockGuardPolicy(statement);
-                attachCancelationSignal(cancelationSignal);
+                attachCancellationSignal(cancellationSignal);
                 try {
                     nativeExecute(mConnectionPtr, statement.mStatementPtr);
                 } finally {
-                    detachCancelationSignal(cancelationSignal);
+                    detachCancellationSignal(cancellationSignal);
                 }
             } finally {
                 releasePreparedStatement(statement);
@@ -396,7 +396,7 @@
      *
      * @param sql The SQL statement to execute.
      * @param bindArgs The arguments to bind, or null if none.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * @return The value of the first column in the first row of the result set
      * as a <code>long</code>, or zero if none.
      *
@@ -405,7 +405,7 @@
      * @throws OperationCanceledException if the operation was canceled.
      */
     public long executeForLong(String sql, Object[] bindArgs,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         if (sql == null) {
             throw new IllegalArgumentException("sql must not be null.");
         }
@@ -417,11 +417,11 @@
                 throwIfStatementForbidden(statement);
                 bindArguments(statement, bindArgs);
                 applyBlockGuardPolicy(statement);
-                attachCancelationSignal(cancelationSignal);
+                attachCancellationSignal(cancellationSignal);
                 try {
                     return nativeExecuteForLong(mConnectionPtr, statement.mStatementPtr);
                 } finally {
-                    detachCancelationSignal(cancelationSignal);
+                    detachCancellationSignal(cancellationSignal);
                 }
             } finally {
                 releasePreparedStatement(statement);
@@ -439,7 +439,7 @@
      *
      * @param sql The SQL statement to execute.
      * @param bindArgs The arguments to bind, or null if none.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * @return The value of the first column in the first row of the result set
      * as a <code>String</code>, or null if none.
      *
@@ -448,7 +448,7 @@
      * @throws OperationCanceledException if the operation was canceled.
      */
     public String executeForString(String sql, Object[] bindArgs,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         if (sql == null) {
             throw new IllegalArgumentException("sql must not be null.");
         }
@@ -460,11 +460,11 @@
                 throwIfStatementForbidden(statement);
                 bindArguments(statement, bindArgs);
                 applyBlockGuardPolicy(statement);
-                attachCancelationSignal(cancelationSignal);
+                attachCancellationSignal(cancellationSignal);
                 try {
                     return nativeExecuteForString(mConnectionPtr, statement.mStatementPtr);
                 } finally {
-                    detachCancelationSignal(cancelationSignal);
+                    detachCancellationSignal(cancellationSignal);
                 }
             } finally {
                 releasePreparedStatement(statement);
@@ -483,7 +483,7 @@
      *
      * @param sql The SQL statement to execute.
      * @param bindArgs The arguments to bind, or null if none.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * @return The file descriptor for a shared memory region that contains
      * the value of the first column in the first row of the result set as a BLOB,
      * or null if none.
@@ -493,7 +493,7 @@
      * @throws OperationCanceledException if the operation was canceled.
      */
     public ParcelFileDescriptor executeForBlobFileDescriptor(String sql, Object[] bindArgs,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         if (sql == null) {
             throw new IllegalArgumentException("sql must not be null.");
         }
@@ -506,13 +506,13 @@
                 throwIfStatementForbidden(statement);
                 bindArguments(statement, bindArgs);
                 applyBlockGuardPolicy(statement);
-                attachCancelationSignal(cancelationSignal);
+                attachCancellationSignal(cancellationSignal);
                 try {
                     int fd = nativeExecuteForBlobFileDescriptor(
                             mConnectionPtr, statement.mStatementPtr);
                     return fd >= 0 ? ParcelFileDescriptor.adoptFd(fd) : null;
                 } finally {
-                    detachCancelationSignal(cancelationSignal);
+                    detachCancellationSignal(cancellationSignal);
                 }
             } finally {
                 releasePreparedStatement(statement);
@@ -531,7 +531,7 @@
      *
      * @param sql The SQL statement to execute.
      * @param bindArgs The arguments to bind, or null if none.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * @return The number of rows that were changed.
      *
      * @throws SQLiteException if an error occurs, such as a syntax error
@@ -539,7 +539,7 @@
      * @throws OperationCanceledException if the operation was canceled.
      */
     public int executeForChangedRowCount(String sql, Object[] bindArgs,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         if (sql == null) {
             throw new IllegalArgumentException("sql must not be null.");
         }
@@ -552,12 +552,12 @@
                 throwIfStatementForbidden(statement);
                 bindArguments(statement, bindArgs);
                 applyBlockGuardPolicy(statement);
-                attachCancelationSignal(cancelationSignal);
+                attachCancellationSignal(cancellationSignal);
                 try {
                     return nativeExecuteForChangedRowCount(
                             mConnectionPtr, statement.mStatementPtr);
                 } finally {
-                    detachCancelationSignal(cancelationSignal);
+                    detachCancellationSignal(cancellationSignal);
                 }
             } finally {
                 releasePreparedStatement(statement);
@@ -576,7 +576,7 @@
      *
      * @param sql The SQL statement to execute.
      * @param bindArgs The arguments to bind, or null if none.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * @return The row id of the last row that was inserted, or 0 if none.
      *
      * @throws SQLiteException if an error occurs, such as a syntax error
@@ -584,7 +584,7 @@
      * @throws OperationCanceledException if the operation was canceled.
      */
     public long executeForLastInsertedRowId(String sql, Object[] bindArgs,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         if (sql == null) {
             throw new IllegalArgumentException("sql must not be null.");
         }
@@ -597,12 +597,12 @@
                 throwIfStatementForbidden(statement);
                 bindArguments(statement, bindArgs);
                 applyBlockGuardPolicy(statement);
-                attachCancelationSignal(cancelationSignal);
+                attachCancellationSignal(cancellationSignal);
                 try {
                     return nativeExecuteForLastInsertedRowId(
                             mConnectionPtr, statement.mStatementPtr);
                 } finally {
-                    detachCancelationSignal(cancelationSignal);
+                    detachCancellationSignal(cancellationSignal);
                 }
             } finally {
                 releasePreparedStatement(statement);
@@ -629,7 +629,7 @@
      * so that it does.  Must be greater than or equal to <code>startPos</code>.
      * @param countAllRows True to count all rows that the query would return
      * regagless of whether they fit in the window.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * @return The number of rows that were counted during query execution.  Might
      * not be all rows in the result set unless <code>countAllRows</code> is true.
      *
@@ -639,7 +639,7 @@
      */
     public int executeForCursorWindow(String sql, Object[] bindArgs,
             CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         if (sql == null) {
             throw new IllegalArgumentException("sql must not be null.");
         }
@@ -658,7 +658,7 @@
                 throwIfStatementForbidden(statement);
                 bindArguments(statement, bindArgs);
                 applyBlockGuardPolicy(statement);
-                attachCancelationSignal(cancelationSignal);
+                attachCancellationSignal(cancellationSignal);
                 try {
                     final long result = nativeExecuteForCursorWindow(
                             mConnectionPtr, statement.mStatementPtr, window.mWindowPtr,
@@ -669,7 +669,7 @@
                     window.setStartPosition(actualPos);
                     return countedRows;
                 } finally {
-                    detachCancelationSignal(cancelationSignal);
+                    detachCancellationSignal(cancellationSignal);
                 }
             } finally {
                 releasePreparedStatement(statement);
@@ -751,40 +751,40 @@
         recyclePreparedStatement(statement);
     }
 
-    private void attachCancelationSignal(CancelationSignal cancelationSignal) {
-        if (cancelationSignal != null) {
-            cancelationSignal.throwIfCanceled();
+    private void attachCancellationSignal(CancellationSignal cancellationSignal) {
+        if (cancellationSignal != null) {
+            cancellationSignal.throwIfCanceled();
 
-            mCancelationSignalAttachCount += 1;
-            if (mCancelationSignalAttachCount == 1) {
-                // Reset cancelation flag before executing the statement.
+            mCancellationSignalAttachCount += 1;
+            if (mCancellationSignalAttachCount == 1) {
+                // Reset cancellation flag before executing the statement.
                 nativeResetCancel(mConnectionPtr, true /*cancelable*/);
 
                 // After this point, onCancel() may be called concurrently.
-                cancelationSignal.setOnCancelListener(this);
+                cancellationSignal.setOnCancelListener(this);
             }
         }
     }
 
-    private void detachCancelationSignal(CancelationSignal cancelationSignal) {
-        if (cancelationSignal != null) {
-            assert mCancelationSignalAttachCount > 0;
+    private void detachCancellationSignal(CancellationSignal cancellationSignal) {
+        if (cancellationSignal != null) {
+            assert mCancellationSignalAttachCount > 0;
 
-            mCancelationSignalAttachCount -= 1;
-            if (mCancelationSignalAttachCount == 0) {
+            mCancellationSignalAttachCount -= 1;
+            if (mCancellationSignalAttachCount == 0) {
                 // After this point, onCancel() cannot be called concurrently.
-                cancelationSignal.setOnCancelListener(null);
+                cancellationSignal.setOnCancelListener(null);
 
-                // Reset cancelation flag after executing the statement.
+                // Reset cancellation flag after executing the statement.
                 nativeResetCancel(mConnectionPtr, false /*cancelable*/);
             }
         }
     }
 
-    // CancelationSignal.OnCancelationListener callback.
+    // CancellationSignal.OnCancelListener callback.
     // This method may be called on a different thread than the executing statement.
-    // However, it will only be called between calls to attachCancelationSignal and
-    // detachCancelationSignal, while a statement is executing.  We can safely assume
+    // However, it will only be called between calls to attachCancellationSignal and
+    // detachCancellationSignal, while a statement is executing.  We can safely assume
     // that the SQLite connection is still alive.
     @Override
     public void onCancel() {
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index d335738..236948e 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -18,7 +18,7 @@
 
 import dalvik.system.CloseGuard;
 
-import android.content.CancelationSignal;
+import android.content.CancellationSignal;
 import android.content.OperationCanceledException;
 import android.database.sqlite.SQLiteDebug.DbStats;
 import android.os.SystemClock;
@@ -284,7 +284,7 @@
      * @param sql If not null, try to find a connection that already has
      * the specified SQL statement in its prepared statement cache.
      * @param connectionFlags The connection request flags.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * @return The connection that was acquired, never null.
      *
      * @throws IllegalStateException if the pool has been closed.
@@ -292,8 +292,8 @@
      * @throws OperationCanceledException if the operation was canceled.
      */
     public SQLiteConnection acquireConnection(String sql, int connectionFlags,
-            CancelationSignal cancelationSignal) {
-        return waitForConnection(sql, connectionFlags, cancelationSignal);
+            CancellationSignal cancellationSignal) {
+        return waitForConnection(sql, connectionFlags, cancellationSignal);
     }
 
     /**
@@ -503,7 +503,7 @@
 
     // Might throw.
     private SQLiteConnection waitForConnection(String sql, int connectionFlags,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         final boolean wantPrimaryConnection =
                 (connectionFlags & CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY) != 0;
 
@@ -512,8 +512,8 @@
             throwIfClosedLocked();
 
             // Abort if canceled.
-            if (cancelationSignal != null) {
-                cancelationSignal.throwIfCanceled();
+            if (cancellationSignal != null) {
+                cancellationSignal.throwIfCanceled();
             }
 
             // Try to acquire a connection.
@@ -550,9 +550,9 @@
                 mConnectionWaiterQueue = waiter;
             }
 
-            if (cancelationSignal != null) {
+            if (cancellationSignal != null) {
                 final int nonce = waiter.mNonce;
-                cancelationSignal.setOnCancelListener(new CancelationSignal.OnCancelListener() {
+                cancellationSignal.setOnCancelListener(new CancellationSignal.OnCancelListener() {
                     @Override
                     public void onCancel() {
                         synchronized (mLock) {
@@ -588,8 +588,8 @@
                 final SQLiteConnection connection = waiter.mAssignedConnection;
                 final RuntimeException ex = waiter.mException;
                 if (connection != null || ex != null) {
-                    if (cancelationSignal != null) {
-                        cancelationSignal.setOnCancelListener(null);
+                    if (cancellationSignal != null) {
+                        cancellationSignal.setOnCancelListener(null);
                     }
                     recycleConnectionWaiterLocked(waiter);
                     if (connection != null) {
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 7db7bfb..505f83e 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -16,7 +16,7 @@
 
 package android.database.sqlite;
 
-import android.content.CancelationSignal;
+import android.content.CancellationSignal;
 import android.content.ContentValues;
 import android.content.OperationCanceledException;
 import android.content.res.Resources;
@@ -967,7 +967,7 @@
      *            default sort order, which may be unordered.
      * @param limit Limits the number of rows returned by the query,
      *            formatted as LIMIT clause. Passing null denotes no LIMIT clause.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * If the operation is canceled, then {@link OperationCanceledException} will be thrown
      * when the query is executed.
      * @return A {@link Cursor} object, which is positioned before the first entry. Note that
@@ -976,9 +976,9 @@
      */
     public Cursor query(boolean distinct, String table, String[] columns,
             String selection, String[] selectionArgs, String groupBy,
-            String having, String orderBy, String limit, CancelationSignal cancelationSignal) {
+            String having, String orderBy, String limit, CancellationSignal cancellationSignal) {
         return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,
-                groupBy, having, orderBy, limit, cancelationSignal);
+                groupBy, having, orderBy, limit, cancellationSignal);
     }
 
     /**
@@ -1049,7 +1049,7 @@
      *            default sort order, which may be unordered.
      * @param limit Limits the number of rows returned by the query,
      *            formatted as LIMIT clause. Passing null denotes no LIMIT clause.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * If the operation is canceled, then {@link OperationCanceledException} will be thrown
      * when the query is executed.
      * @return A {@link Cursor} object, which is positioned before the first entry. Note that
@@ -1059,13 +1059,13 @@
     public Cursor queryWithFactory(CursorFactory cursorFactory,
             boolean distinct, String table, String[] columns,
             String selection, String[] selectionArgs, String groupBy,
-            String having, String orderBy, String limit, CancelationSignal cancelationSignal) {
+            String having, String orderBy, String limit, CancellationSignal cancellationSignal) {
         throwIfNotOpen(); // fail fast
         String sql = SQLiteQueryBuilder.buildQueryString(
                 distinct, table, columns, selection, groupBy, having, orderBy, limit);
 
         return rawQueryWithFactory(cursorFactory, sql, selectionArgs,
-                findEditTable(table), cancelationSignal);
+                findEditTable(table), cancellationSignal);
     }
 
     /**
@@ -1163,15 +1163,15 @@
      * @param selectionArgs You may include ?s in where clause in the query,
      *     which will be replaced by the values from selectionArgs. The
      *     values will be bound as Strings.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * If the operation is canceled, then {@link OperationCanceledException} will be thrown
      * when the query is executed.
      * @return A {@link Cursor} object, which is positioned before the first entry. Note that
      * {@link Cursor}s are not synchronized, see the documentation for more details.
      */
     public Cursor rawQuery(String sql, String[] selectionArgs,
-            CancelationSignal cancelationSignal) {
-        return rawQueryWithFactory(null, sql, selectionArgs, null, cancelationSignal);
+            CancellationSignal cancellationSignal) {
+        return rawQueryWithFactory(null, sql, selectionArgs, null, cancellationSignal);
     }
 
     /**
@@ -1201,7 +1201,7 @@
      *     which will be replaced by the values from selectionArgs. The
      *     values will be bound as Strings.
      * @param editTable the name of the first table, which is editable
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * If the operation is canceled, then {@link OperationCanceledException} will be thrown
      * when the query is executed.
      * @return A {@link Cursor} object, which is positioned before the first entry. Note that
@@ -1209,11 +1209,11 @@
      */
     public Cursor rawQueryWithFactory(
             CursorFactory cursorFactory, String sql, String[] selectionArgs,
-            String editTable, CancelationSignal cancelationSignal) {
+            String editTable, CancellationSignal cancellationSignal) {
         throwIfNotOpen(); // fail fast
 
         SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,
-                cancelationSignal);
+                cancellationSignal);
         return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory,
                 selectionArgs);
     }
diff --git a/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java b/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java
index c490dc6..3375e74 100644
--- a/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java
+++ b/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java
@@ -16,7 +16,7 @@
 
 package android.database.sqlite;
 
-import android.content.CancelationSignal;
+import android.content.CancellationSignal;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 
@@ -29,19 +29,19 @@
     private final SQLiteDatabase mDatabase;
     private final String mEditTable; 
     private final String mSql;
-    private final CancelationSignal mCancelationSignal;
+    private final CancellationSignal mCancellationSignal;
     private SQLiteQuery mQuery;
 
     public SQLiteDirectCursorDriver(SQLiteDatabase db, String sql, String editTable,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         mDatabase = db;
         mEditTable = editTable;
         mSql = sql;
-        mCancelationSignal = cancelationSignal;
+        mCancellationSignal = cancellationSignal;
     }
 
     public Cursor query(CursorFactory factory, String[] selectionArgs) {
-        final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancelationSignal);
+        final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancellationSignal);
         final Cursor cursor;
         try {
             query.bindAllArgsAsStrings(selectionArgs);
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index f3da2a6..9f0edfb 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -16,7 +16,7 @@
 
 package android.database.sqlite;
 
-import android.content.CancelationSignal;
+import android.content.CancellationSignal;
 import android.database.DatabaseUtils;
 
 import java.util.Arrays;
@@ -38,7 +38,7 @@
     private final Object[] mBindArgs;
 
     SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs,
-            CancelationSignal cancelationSignalForPrepare) {
+            CancellationSignal cancellationSignalForPrepare) {
         mDatabase = db;
         mSql = sql.trim();
 
@@ -57,7 +57,7 @@
                 SQLiteStatementInfo info = new SQLiteStatementInfo();
                 db.getThreadSession().prepare(mSql,
                         db.getThreadDefaultConnectionFlags(assumeReadOnly),
-                        cancelationSignalForPrepare, info);
+                        cancellationSignalForPrepare, info);
                 mReadOnly = info.readOnly;
                 mColumnNames = info.columnNames;
                 mNumParameters = info.numParameters;
diff --git a/core/java/android/database/sqlite/SQLiteQuery.java b/core/java/android/database/sqlite/SQLiteQuery.java
index df2e260..30e77b5 100644
--- a/core/java/android/database/sqlite/SQLiteQuery.java
+++ b/core/java/android/database/sqlite/SQLiteQuery.java
@@ -16,7 +16,7 @@
 
 package android.database.sqlite;
 
-import android.content.CancelationSignal;
+import android.content.CancellationSignal;
 import android.content.OperationCanceledException;
 import android.database.CursorWindow;
 import android.util.Log;
@@ -31,12 +31,12 @@
 public final class SQLiteQuery extends SQLiteProgram {
     private static final String TAG = "SQLiteQuery";
 
-    private final CancelationSignal mCancelationSignal;
+    private final CancellationSignal mCancellationSignal;
 
-    SQLiteQuery(SQLiteDatabase db, String query, CancelationSignal cancelationSignal) {
-        super(db, query, null, cancelationSignal);
+    SQLiteQuery(SQLiteDatabase db, String query, CancellationSignal cancellationSignal) {
+        super(db, query, null, cancellationSignal);
 
-        mCancelationSignal = cancelationSignal;
+        mCancellationSignal = cancellationSignal;
     }
 
     /**
@@ -61,7 +61,7 @@
             try {
                 int numRows = getSession().executeForCursorWindow(getSql(), getBindArgs(),
                         window, startPos, requiredPos, countAllRows, getConnectionFlags(),
-                        mCancelationSignal);
+                        mCancellationSignal);
                 return numRows;
             } catch (SQLiteDatabaseCorruptException ex) {
                 onCorruption();
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index 89469cb..6f84b5e 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -16,7 +16,7 @@
 
 package android.database.sqlite;
 
-import android.content.CancelationSignal;
+import android.content.CancellationSignal;
 import android.content.OperationCanceledException;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
@@ -292,7 +292,7 @@
             String selection, String[] selectionArgs, String groupBy,
             String having, String sortOrder) {
         return query(db, projectionIn, selection, selectionArgs, groupBy, having, sortOrder,
-                null /* limit */, null /* cancelationSignal */);
+                null /* limit */, null /* cancellationSignal */);
     }
 
     /**
@@ -362,7 +362,7 @@
      *   will use the default sort order, which may be unordered.
      * @param limit Limits the number of rows returned by the query,
      *   formatted as LIMIT clause. Passing null denotes no LIMIT clause.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * If the operation is canceled, then {@link OperationCanceledException} will be thrown
      * when the query is executed.
      * @return a cursor over the result set
@@ -371,7 +371,7 @@
      */
     public Cursor query(SQLiteDatabase db, String[] projectionIn,
             String selection, String[] selectionArgs, String groupBy,
-            String having, String sortOrder, String limit, CancelationSignal cancelationSignal) {
+            String having, String sortOrder, String limit, CancellationSignal cancellationSignal) {
         if (mTables == null) {
             return null;
         }
@@ -387,7 +387,7 @@
             String sqlForValidation = buildQuery(projectionIn, "(" + selection + ")", groupBy,
                     having, sortOrder, limit);
             validateQuerySql(db, sqlForValidation,
-                    cancelationSignal); // will throw if query is invalid
+                    cancellationSignal); // will throw if query is invalid
         }
 
         String sql = buildQuery(
@@ -400,7 +400,7 @@
         return db.rawQueryWithFactory(
                 mFactory, sql, selectionArgs,
                 SQLiteDatabase.findEditTable(mTables),
-                cancelationSignal); // will throw if query is invalid
+                cancellationSignal); // will throw if query is invalid
     }
 
     /**
@@ -408,9 +408,9 @@
      * If the SQL statement is not valid, this method will throw a {@link SQLiteException}.
      */
     private void validateQuerySql(SQLiteDatabase db, String sql,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         db.getThreadSession().prepare(sql,
-                db.getThreadDefaultConnectionFlags(true /*readOnly*/), cancelationSignal, null);
+                db.getThreadDefaultConnectionFlags(true /*readOnly*/), cancellationSignal, null);
     }
 
     /**
diff --git a/core/java/android/database/sqlite/SQLiteSession.java b/core/java/android/database/sqlite/SQLiteSession.java
index b5a3e31..43efb03 100644
--- a/core/java/android/database/sqlite/SQLiteSession.java
+++ b/core/java/android/database/sqlite/SQLiteSession.java
@@ -16,7 +16,7 @@
 
 package android.database.sqlite;
 
-import android.content.CancelationSignal;
+import android.content.CancellationSignal;
 import android.content.OperationCanceledException;
 import android.database.CursorWindow;
 import android.database.DatabaseUtils;
@@ -280,7 +280,7 @@
      * @param transactionListener The transaction listener, or null if none.
      * @param connectionFlags The connection flags to use if a connection must be
      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      *
      * @throws IllegalStateException if {@link #setTransactionSuccessful} has already been
      * called for the current transaction.
@@ -293,21 +293,21 @@
      */
     public void beginTransaction(int transactionMode,
             SQLiteTransactionListener transactionListener, int connectionFlags,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         throwIfTransactionMarkedSuccessful();
         beginTransactionUnchecked(transactionMode, transactionListener, connectionFlags,
-                cancelationSignal);
+                cancellationSignal);
     }
 
     private void beginTransactionUnchecked(int transactionMode,
             SQLiteTransactionListener transactionListener, int connectionFlags,
-            CancelationSignal cancelationSignal) {
-        if (cancelationSignal != null) {
-            cancelationSignal.throwIfCanceled();
+            CancellationSignal cancellationSignal) {
+        if (cancellationSignal != null) {
+            cancellationSignal.throwIfCanceled();
         }
 
         if (mTransactionStack == null) {
-            acquireConnection(null, connectionFlags, cancelationSignal); // might throw
+            acquireConnection(null, connectionFlags, cancellationSignal); // might throw
         }
         try {
             // Set up the transaction such that we can back out safely
@@ -317,14 +317,14 @@
                 switch (transactionMode) {
                     case TRANSACTION_MODE_IMMEDIATE:
                         mConnection.execute("BEGIN IMMEDIATE;", null,
-                                cancelationSignal); // might throw
+                                cancellationSignal); // might throw
                         break;
                     case TRANSACTION_MODE_EXCLUSIVE:
                         mConnection.execute("BEGIN EXCLUSIVE;", null,
-                                cancelationSignal); // might throw
+                                cancellationSignal); // might throw
                         break;
                     default:
-                        mConnection.execute("BEGIN;", null, cancelationSignal); // might throw
+                        mConnection.execute("BEGIN;", null, cancellationSignal); // might throw
                         break;
                 }
             }
@@ -335,7 +335,7 @@
                     transactionListener.onBegin(); // might throw
                 } catch (RuntimeException ex) {
                     if (mTransactionStack == null) {
-                        mConnection.execute("ROLLBACK;", null, cancelationSignal); // might throw
+                        mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
                     }
                     throw ex;
                 }
@@ -384,7 +384,7 @@
      * This method must be called exactly once for each call to {@link #beginTransaction}.
      * </p>
      *
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      *
      * @throws IllegalStateException if there is no current transaction.
      * @throws SQLiteException if an error occurs.
@@ -394,16 +394,16 @@
      * @see #setTransactionSuccessful
      * @see #yieldTransaction
      */
-    public void endTransaction(CancelationSignal cancelationSignal) {
+    public void endTransaction(CancellationSignal cancellationSignal) {
         throwIfNoTransaction();
         assert mConnection != null;
 
-        endTransactionUnchecked(cancelationSignal);
+        endTransactionUnchecked(cancellationSignal);
     }
 
-    private void endTransactionUnchecked(CancelationSignal cancelationSignal) {
-        if (cancelationSignal != null) {
-            cancelationSignal.throwIfCanceled();
+    private void endTransactionUnchecked(CancellationSignal cancellationSignal) {
+        if (cancellationSignal != null) {
+            cancellationSignal.throwIfCanceled();
         }
 
         final Transaction top = mTransactionStack;
@@ -434,9 +434,9 @@
         } else {
             try {
                 if (successful) {
-                    mConnection.execute("COMMIT;", null, cancelationSignal); // might throw
+                    mConnection.execute("COMMIT;", null, cancellationSignal); // might throw
                 } else {
-                    mConnection.execute("ROLLBACK;", null, cancelationSignal); // might throw
+                    mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
                 }
             } finally {
                 releaseConnection(); // might throw
@@ -487,7 +487,7 @@
      * @param throwIfUnsafe If true, then instead of returning false when no
      * transaction is in progress, a nested transaction is in progress, or when
      * the transaction has already been marked successful, throws {@link IllegalStateException}.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * @return True if the transaction was actually yielded.
      *
      * @throws IllegalStateException if <code>throwIfNested</code> is true and
@@ -500,7 +500,7 @@
      * @see #endTransaction
      */
     public boolean yieldTransaction(long sleepAfterYieldDelayMillis, boolean throwIfUnsafe,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         if (throwIfUnsafe) {
             throwIfNoTransaction();
             throwIfTransactionMarkedSuccessful();
@@ -518,13 +518,13 @@
         }
 
         return yieldTransactionUnchecked(sleepAfterYieldDelayMillis,
-                cancelationSignal); // might throw
+                cancellationSignal); // might throw
     }
 
     private boolean yieldTransactionUnchecked(long sleepAfterYieldDelayMillis,
-            CancelationSignal cancelationSignal) {
-        if (cancelationSignal != null) {
-            cancelationSignal.throwIfCanceled();
+            CancellationSignal cancellationSignal) {
+        if (cancellationSignal != null) {
+            cancellationSignal.throwIfCanceled();
         }
 
         if (!mConnectionPool.shouldYieldConnection(mConnection, mConnectionFlags)) {
@@ -534,7 +534,7 @@
         final int transactionMode = mTransactionStack.mMode;
         final SQLiteTransactionListener listener = mTransactionStack.mListener;
         final int connectionFlags = mConnectionFlags;
-        endTransactionUnchecked(cancelationSignal); // might throw
+        endTransactionUnchecked(cancellationSignal); // might throw
 
         if (sleepAfterYieldDelayMillis > 0) {
             try {
@@ -545,7 +545,7 @@
         }
 
         beginTransactionUnchecked(transactionMode, listener, connectionFlags,
-                cancelationSignal); // might throw
+                cancellationSignal); // might throw
         return true;
     }
 
@@ -566,24 +566,24 @@
      * @param sql The SQL statement to prepare.
      * @param connectionFlags The connection flags to use if a connection must be
      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * @param outStatementInfo The {@link SQLiteStatementInfo} object to populate
      * with information about the statement, or null if none.
      *
      * @throws SQLiteException if an error occurs, such as a syntax error.
      * @throws OperationCanceledException if the operation was canceled.
      */
-    public void prepare(String sql, int connectionFlags, CancelationSignal cancelationSignal,
+    public void prepare(String sql, int connectionFlags, CancellationSignal cancellationSignal,
             SQLiteStatementInfo outStatementInfo) {
         if (sql == null) {
             throw new IllegalArgumentException("sql must not be null.");
         }
 
-        if (cancelationSignal != null) {
-            cancelationSignal.throwIfCanceled();
+        if (cancellationSignal != null) {
+            cancellationSignal.throwIfCanceled();
         }
 
-        acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
+        acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
         try {
             mConnection.prepare(sql, outStatementInfo); // might throw
         } finally {
@@ -598,25 +598,25 @@
      * @param bindArgs The arguments to bind, or null if none.
      * @param connectionFlags The connection flags to use if a connection must be
      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      *
      * @throws SQLiteException if an error occurs, such as a syntax error
      * or invalid number of bind arguments.
      * @throws OperationCanceledException if the operation was canceled.
      */
     public void execute(String sql, Object[] bindArgs, int connectionFlags,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         if (sql == null) {
             throw new IllegalArgumentException("sql must not be null.");
         }
 
-        if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
+        if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
             return;
         }
 
-        acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
+        acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
         try {
-            mConnection.execute(sql, bindArgs, cancelationSignal); // might throw
+            mConnection.execute(sql, bindArgs, cancellationSignal); // might throw
         } finally {
             releaseConnection(); // might throw
         }
@@ -629,7 +629,7 @@
      * @param bindArgs The arguments to bind, or null if none.
      * @param connectionFlags The connection flags to use if a connection must be
      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * @return The value of the first column in the first row of the result set
      * as a <code>long</code>, or zero if none.
      *
@@ -638,18 +638,18 @@
      * @throws OperationCanceledException if the operation was canceled.
      */
     public long executeForLong(String sql, Object[] bindArgs, int connectionFlags,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         if (sql == null) {
             throw new IllegalArgumentException("sql must not be null.");
         }
 
-        if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
+        if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
             return 0;
         }
 
-        acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
+        acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
         try {
-            return mConnection.executeForLong(sql, bindArgs, cancelationSignal); // might throw
+            return mConnection.executeForLong(sql, bindArgs, cancellationSignal); // might throw
         } finally {
             releaseConnection(); // might throw
         }
@@ -662,7 +662,7 @@
      * @param bindArgs The arguments to bind, or null if none.
      * @param connectionFlags The connection flags to use if a connection must be
      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * @return The value of the first column in the first row of the result set
      * as a <code>String</code>, or null if none.
      *
@@ -671,18 +671,18 @@
      * @throws OperationCanceledException if the operation was canceled.
      */
     public String executeForString(String sql, Object[] bindArgs, int connectionFlags,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         if (sql == null) {
             throw new IllegalArgumentException("sql must not be null.");
         }
 
-        if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
+        if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
             return null;
         }
 
-        acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
+        acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
         try {
-            return mConnection.executeForString(sql, bindArgs, cancelationSignal); // might throw
+            return mConnection.executeForString(sql, bindArgs, cancellationSignal); // might throw
         } finally {
             releaseConnection(); // might throw
         }
@@ -696,7 +696,7 @@
      * @param bindArgs The arguments to bind, or null if none.
      * @param connectionFlags The connection flags to use if a connection must be
      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * @return The file descriptor for a shared memory region that contains
      * the value of the first column in the first row of the result set as a BLOB,
      * or null if none.
@@ -706,19 +706,19 @@
      * @throws OperationCanceledException if the operation was canceled.
      */
     public ParcelFileDescriptor executeForBlobFileDescriptor(String sql, Object[] bindArgs,
-            int connectionFlags, CancelationSignal cancelationSignal) {
+            int connectionFlags, CancellationSignal cancellationSignal) {
         if (sql == null) {
             throw new IllegalArgumentException("sql must not be null.");
         }
 
-        if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
+        if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
             return null;
         }
 
-        acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
+        acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
         try {
             return mConnection.executeForBlobFileDescriptor(sql, bindArgs,
-                    cancelationSignal); // might throw
+                    cancellationSignal); // might throw
         } finally {
             releaseConnection(); // might throw
         }
@@ -732,7 +732,7 @@
      * @param bindArgs The arguments to bind, or null if none.
      * @param connectionFlags The connection flags to use if a connection must be
      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * @return The number of rows that were changed.
      *
      * @throws SQLiteException if an error occurs, such as a syntax error
@@ -740,19 +740,19 @@
      * @throws OperationCanceledException if the operation was canceled.
      */
     public int executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         if (sql == null) {
             throw new IllegalArgumentException("sql must not be null.");
         }
 
-        if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
+        if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
             return 0;
         }
 
-        acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
+        acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
         try {
             return mConnection.executeForChangedRowCount(sql, bindArgs,
-                    cancelationSignal); // might throw
+                    cancellationSignal); // might throw
         } finally {
             releaseConnection(); // might throw
         }
@@ -766,7 +766,7 @@
      * @param bindArgs The arguments to bind, or null if none.
      * @param connectionFlags The connection flags to use if a connection must be
      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * @return The row id of the last row that was inserted, or 0 if none.
      *
      * @throws SQLiteException if an error occurs, such as a syntax error
@@ -774,19 +774,19 @@
      * @throws OperationCanceledException if the operation was canceled.
      */
     public long executeForLastInsertedRowId(String sql, Object[] bindArgs, int connectionFlags,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         if (sql == null) {
             throw new IllegalArgumentException("sql must not be null.");
         }
 
-        if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
+        if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
             return 0;
         }
 
-        acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
+        acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
         try {
             return mConnection.executeForLastInsertedRowId(sql, bindArgs,
-                    cancelationSignal); // might throw
+                    cancellationSignal); // might throw
         } finally {
             releaseConnection(); // might throw
         }
@@ -808,7 +808,7 @@
      * regagless of whether they fit in the window.
      * @param connectionFlags The connection flags to use if a connection must be
      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * @return The number of rows that were counted during query execution.  Might
      * not be all rows in the result set unless <code>countAllRows</code> is true.
      *
@@ -818,7 +818,7 @@
      */
     public int executeForCursorWindow(String sql, Object[] bindArgs,
             CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
-            int connectionFlags, CancelationSignal cancelationSignal) {
+            int connectionFlags, CancellationSignal cancellationSignal) {
         if (sql == null) {
             throw new IllegalArgumentException("sql must not be null.");
         }
@@ -826,16 +826,16 @@
             throw new IllegalArgumentException("window must not be null.");
         }
 
-        if (executeSpecial(sql, bindArgs, connectionFlags, cancelationSignal)) {
+        if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
             window.clear();
             return 0;
         }
 
-        acquireConnection(sql, connectionFlags, cancelationSignal); // might throw
+        acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
         try {
             return mConnection.executeForCursorWindow(sql, bindArgs,
                     window, startPos, requiredPos, countAllRows,
-                    cancelationSignal); // might throw
+                    cancellationSignal); // might throw
         } finally {
             releaseConnection(); // might throw
         }
@@ -854,7 +854,7 @@
      * @param bindArgs The arguments to bind, or null if none.
      * @param connectionFlags The connection flags to use if a connection must be
      * acquired by this operation.  Refer to {@link SQLiteConnectionPool}.
-     * @param cancelationSignal A signal to cancel the operation in progress, or null if none.
+     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
      * @return True if the statement was of a special form that was handled here,
      * false otherwise.
      *
@@ -863,36 +863,36 @@
      * @throws OperationCanceledException if the operation was canceled.
      */
     private boolean executeSpecial(String sql, Object[] bindArgs, int connectionFlags,
-            CancelationSignal cancelationSignal) {
-        if (cancelationSignal != null) {
-            cancelationSignal.throwIfCanceled();
+            CancellationSignal cancellationSignal) {
+        if (cancellationSignal != null) {
+            cancellationSignal.throwIfCanceled();
         }
 
         final int type = DatabaseUtils.getSqlStatementType(sql);
         switch (type) {
             case DatabaseUtils.STATEMENT_BEGIN:
                 beginTransaction(TRANSACTION_MODE_EXCLUSIVE, null, connectionFlags,
-                        cancelationSignal);
+                        cancellationSignal);
                 return true;
 
             case DatabaseUtils.STATEMENT_COMMIT:
                 setTransactionSuccessful();
-                endTransaction(cancelationSignal);
+                endTransaction(cancellationSignal);
                 return true;
 
             case DatabaseUtils.STATEMENT_ABORT:
-                endTransaction(cancelationSignal);
+                endTransaction(cancellationSignal);
                 return true;
         }
         return false;
     }
 
     private void acquireConnection(String sql, int connectionFlags,
-            CancelationSignal cancelationSignal) {
+            CancellationSignal cancellationSignal) {
         if (mConnection == null) {
             assert mConnectionUseCount == 0;
             mConnection = mConnectionPool.acquireConnection(sql, connectionFlags,
-                    cancelationSignal); // might throw
+                    cancellationSignal); // might throw
             mConnectionFlags = connectionFlags;
         }
         mConnectionUseCount += 1;
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 633c38e0..442535a 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -43,7 +43,7 @@
     NetworkPolicy[] getNetworkPolicies();
 
     /** Snooze limit on policy matching given template. */
-    void snoozePolicy(in NetworkTemplate template);
+    void snoozeLimit(in NetworkTemplate template);
 
     /** Control if background data is restricted system-wide. */
     void setRestrictBackground(boolean restrictBackground);
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index d9ea700..04cf1a3 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -38,18 +38,25 @@
     public int cycleDay;
     public long warningBytes;
     public long limitBytes;
-    public long lastSnooze;
+    public long lastWarningSnooze;
+    public long lastLimitSnooze;
     public boolean metered;
 
     private static final long DEFAULT_MTU = 1500;
 
-    public NetworkPolicy(NetworkTemplate template, int cycleDay, long warningBytes, long limitBytes,
-            long lastSnooze, boolean metered) {
+    public NetworkPolicy(NetworkTemplate template, int cycleDay, long warningBytes,
+            long limitBytes, boolean metered) {
+        this(template, cycleDay, warningBytes, limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, metered);
+    }
+
+    public NetworkPolicy(NetworkTemplate template, int cycleDay, long warningBytes,
+            long limitBytes, long lastWarningSnooze, long lastLimitSnooze, boolean metered) {
         this.template = checkNotNull(template, "missing NetworkTemplate");
         this.cycleDay = cycleDay;
         this.warningBytes = warningBytes;
         this.limitBytes = limitBytes;
-        this.lastSnooze = lastSnooze;
+        this.lastWarningSnooze = lastWarningSnooze;
+        this.lastLimitSnooze = lastLimitSnooze;
         this.metered = metered;
     }
 
@@ -58,7 +65,8 @@
         cycleDay = in.readInt();
         warningBytes = in.readLong();
         limitBytes = in.readLong();
-        lastSnooze = in.readLong();
+        lastWarningSnooze = in.readLong();
+        lastLimitSnooze = in.readLong();
         metered = in.readInt() != 0;
     }
 
@@ -68,7 +76,8 @@
         dest.writeInt(cycleDay);
         dest.writeLong(warningBytes);
         dest.writeLong(limitBytes);
-        dest.writeLong(lastSnooze);
+        dest.writeLong(lastWarningSnooze);
+        dest.writeLong(lastLimitSnooze);
         dest.writeInt(metered ? 1 : 0);
     }
 
@@ -78,6 +87,13 @@
     }
 
     /**
+     * Test if given measurement is over {@link #warningBytes}.
+     */
+    public boolean isOverWarning(long totalBytes) {
+        return warningBytes != WARNING_DISABLED && totalBytes >= warningBytes;
+    }
+
+    /**
      * Test if given measurement is near enough to {@link #limitBytes} to be
      * considered over-limit.
      */
@@ -88,6 +104,14 @@
         return limitBytes != LIMIT_DISABLED && totalBytes >= limitBytes;
     }
 
+    /**
+     * Clear any existing snooze values, setting to {@link #SNOOZE_NEVER}.
+     */
+    public void clearSnooze() {
+        lastWarningSnooze = SNOOZE_NEVER;
+        lastLimitSnooze = SNOOZE_NEVER;
+    }
+
     /** {@inheritDoc} */
     public int compareTo(NetworkPolicy another) {
         if (another == null || another.limitBytes == LIMIT_DISABLED) {
@@ -103,7 +127,8 @@
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(template, cycleDay, warningBytes, limitBytes, lastSnooze, metered);
+        return Objects.hashCode(template, cycleDay, warningBytes, limitBytes, lastWarningSnooze,
+                lastLimitSnooze, metered);
     }
 
     @Override
@@ -111,8 +136,10 @@
         if (obj instanceof NetworkPolicy) {
             final NetworkPolicy other = (NetworkPolicy) obj;
             return cycleDay == other.cycleDay && warningBytes == other.warningBytes
-                    && limitBytes == other.limitBytes && lastSnooze == other.lastSnooze
-                    && metered == other.metered && Objects.equal(template, other.template);
+                    && limitBytes == other.limitBytes
+                    && lastWarningSnooze == other.lastWarningSnooze
+                    && lastLimitSnooze == other.lastLimitSnooze && metered == other.metered
+                    && Objects.equal(template, other.template);
         }
         return false;
     }
@@ -120,8 +147,9 @@
     @Override
     public String toString() {
         return "NetworkPolicy[" + template + "]: cycleDay=" + cycleDay + ", warningBytes="
-                + warningBytes + ", limitBytes=" + limitBytes + ", lastSnooze=" + lastSnooze
-                + ", metered=" + metered;
+                + warningBytes + ", limitBytes=" + limitBytes + ", lastWarningSnooze="
+                + lastWarningSnooze + ", lastLimitSnooze=" + lastLimitSnooze + ", metered="
+                + metered;
     }
 
     public static final Creator<NetworkPolicy> CREATOR = new Creator<NetworkPolicy>() {
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index dfdea38..973fac1 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -44,6 +44,13 @@
      */
     public final static int UNSUPPORTED = -1;
 
+    /** @hide */
+    public static final long KB_IN_BYTES = 1024;
+    /** @hide */
+    public static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
+    /** @hide */
+    public static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
+
     /**
      * Special UID value used when collecting {@link NetworkStatsHistory} for
      * removed applications.
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 24569fa..577fc43 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -49,6 +49,7 @@
     private static final boolean FIND_POTENTIAL_LEAKS = false;
     private static final String TAG = "Binder";
 
+    /* mObject is used by native code, do not remove or rename */
     private int mObject;
     private IInterface mOwner;
     private String mDescriptor;
@@ -70,7 +71,35 @@
      * incoming transaction, then its own uid is returned.
      */
     public static final native int getCallingUid();
-    
+
+    /**
+     * Return the original ID of the user assigned to the process that sent you the current
+     * transaction that is being processed. This uid can be used with higher-level system services
+     * to determine its identity and check permissions. If the current thread is not currently
+     * executing an incoming transaction, then its own uid is returned.
+     * <p/>
+     * This value cannot be reset by calls to {@link #clearCallingIdentity()}.
+     * @hide
+     */
+    public static final int getOrigCallingUid() {
+        if (UserId.MU_ENABLED) {
+            return getOrigCallingUidNative();
+        } else {
+            return getCallingUid();
+        }
+    }
+
+    private static final native int getOrigCallingUidNative();
+
+    /**
+     * Utility function to return the user id of the calling process.
+     * @return userId of the calling process, extracted from the callingUid
+     * @hide
+     */
+    public static final int getOrigCallingUser() {
+        return UserId.getUserId(getOrigCallingUid());
+    }
+
     /**
      * Reset the identity of the incoming IPC on the current thread.  This can
      * be useful if, while handling an incoming call, you will be calling
diff --git a/core/java/android/os/UserId.java b/core/java/android/os/UserId.java
new file mode 100644
index 0000000..4124d51
--- /dev/null
+++ b/core/java/android/os/UserId.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011 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;
+
+/**
+ * @hide
+ */
+public final class UserId {
+    /**
+     * Range of IDs allocated for a user.
+     *
+     * @hide
+     */
+    public static final int PER_USER_RANGE = 100000;
+
+    public static final int USER_ALL = -1;
+
+    /**
+     * Enable multi-user related side effects. Set this to false if there are problems with single
+     * user usecases.
+     * */
+    public static final boolean MU_ENABLED = true;
+
+    /**
+     * Checks to see if the user id is the same for the two uids, i.e., they belong to the same
+     * user.
+     * @hide
+     */
+    public static final boolean isSameUser(int uid1, int uid2) {
+        return getUserId(uid1) == getUserId(uid2);
+    }
+
+    /**
+     * Checks to see if both uids are referring to the same app id, ignoring the user id part of the
+     * uids.
+     * @param uid1 uid to compare
+     * @param uid2 other uid to compare
+     * @return whether the appId is the same for both uids
+     * @hide
+     */
+    public static final boolean isSameApp(int uid1, int uid2) {
+        return getAppId(uid1) == getAppId(uid2);
+    }
+
+    /**
+     * Returns the user id for a given uid.
+     * @hide
+     */
+    public static final int getUserId(int uid) {
+        if (MU_ENABLED) {
+            return uid / PER_USER_RANGE;
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * Returns the uid that is composed from the userId and the appId.
+     * @hide
+     */
+    public static final int getUid(int userId, int appId) {
+        if (MU_ENABLED) {
+            return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
+        } else {
+            return appId;
+        }
+    }
+
+    /**
+     * Returns the app id (or base uid) for a given uid, stripping out the user id from it.
+     * @hide
+     */
+    public static final int getAppId(int uid) {
+        return uid % PER_USER_RANGE;
+    }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 207810c..375e5e4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2482,6 +2482,11 @@
             Uri.parse("content://" + AUTHORITY + "/secure");
 
         /**
+         * Whether user has enabled development settings.
+         */
+        public static final String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
+
+        /**
          * Whether ADB is enabled.
          */
         public static final String ADB_ENABLED = "adb_enabled";
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index c86ea77..3ee275c 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -27,6 +27,10 @@
 
 /**
  * Coordinates animations and drawing for UI on a particular thread.
+ *
+ * This object is thread-safe.  Other threads can add and remove listeners
+ * or schedule work to occur at a later time on the UI thread.
+ *
  * @hide
  */
 public final class Choreographer extends Handler {
@@ -44,7 +48,7 @@
     private static final long DEFAULT_FRAME_DELAY = 10;
 
     // The number of milliseconds between animation frames.
-    private static long sFrameDelay = DEFAULT_FRAME_DELAY;
+    private static volatile long sFrameDelay = DEFAULT_FRAME_DELAY;
 
     // Thread local storage for the choreographer.
     private static final ThreadLocal<Choreographer> sThreadInstance =
@@ -75,6 +79,8 @@
     private static final int MSG_DO_ANIMATION = 0;
     private static final int MSG_DO_DRAW = 1;
 
+    private final Object mLock = new Object();
+
     private final Looper mLooper;
 
     private OnAnimateListener[] mOnAnimateListeners;
@@ -138,9 +144,14 @@
 
     /**
      * Schedules animation (and drawing) to occur on the next frame synchronization boundary.
-     * Must be called on the UI thread.
      */
     public void scheduleAnimation() {
+        synchronized (mLock) {
+            scheduleAnimationLocked();
+        }
+    }
+
+    private void scheduleAnimationLocked() {
         if (!mAnimationScheduled) {
             mAnimationScheduled = true;
             if (USE_VSYNC) {
@@ -163,12 +174,14 @@
     }
 
     /**
-     * Return true if {@link #scheduleAnimation()} has been called but
+     * Returns true if {@link #scheduleAnimation()} has been called but
      * {@link OnAnimateListener#onAnimate() OnAnimateListener.onAnimate()} has
      * not yet been called.
      */
     public boolean isAnimationScheduled() {
-        return mAnimationScheduled;
+        synchronized (mLock) {
+            return mAnimationScheduled;
+        }
     }
 
     /**
@@ -176,26 +189,30 @@
      * Must be called on the UI thread.
      */
     public void scheduleDraw() {
-        if (!mDrawScheduled) {
-            mDrawScheduled = true;
-            if (USE_ANIMATION_TIMER_FOR_DRAW) {
-                scheduleAnimation();
-            } else {
-                if (DEBUG) {
-                    Log.d(TAG, "Scheduling draw immediately.");
+        synchronized (mLock) {
+            if (!mDrawScheduled) {
+                mDrawScheduled = true;
+                if (USE_ANIMATION_TIMER_FOR_DRAW) {
+                    scheduleAnimationLocked();
+                } else {
+                    if (DEBUG) {
+                        Log.d(TAG, "Scheduling draw immediately.");
+                    }
+                    sendEmptyMessage(MSG_DO_DRAW);
                 }
-                sendEmptyMessage(MSG_DO_DRAW);
             }
         }
     }
 
     /**
-     * Return true if {@link #scheduleDraw()} has been called but
+     * Returns true if {@link #scheduleDraw()} has been called but
      * {@link OnDrawListener#onDraw() OnDrawListener.onDraw()} has
      * not yet been called.
      */
     public boolean isDrawScheduled() {
-        return mDrawScheduled;
+        synchronized (mLock) {
+            return mDrawScheduled;
+        }
     }
 
     @Override
@@ -211,60 +228,75 @@
     }
 
     private void doAnimation() {
-        if (mAnimationScheduled) {
-            mAnimationScheduled = false;
-
-            final long start = SystemClock.uptimeMillis();
-            if (DEBUG) {
-                Log.d(TAG, "Performing animation: " + Math.max(0, start - mLastAnimationTime)
-                        + " ms have elapsed since previous animation.");
-            }
-            mLastAnimationTime = start;
-
-            final OnAnimateListener[] listeners = mOnAnimateListeners;
-            if (listeners != null) {
-                for (int i = 0; i < listeners.length; i++) {
-                    listeners[i].onAnimate();
-                }
-            }
-
-            if (DEBUG) {
-                Log.d(TAG, "Animation took " + (SystemClock.uptimeMillis() - start) + " ms.");
-            }
-        }
+        doAnimationInner();
 
         if (USE_ANIMATION_TIMER_FOR_DRAW) {
             doDraw();
         }
     }
 
+    private void doAnimationInner() {
+        final long start;
+        final OnAnimateListener[] listeners;
+        synchronized (mLock) {
+            if (!mAnimationScheduled) {
+                return; // no work to do
+            }
+            mAnimationScheduled = false;
+
+            start = SystemClock.uptimeMillis();
+            if (DEBUG) {
+                Log.d(TAG, "Performing animation: " + Math.max(0, start - mLastAnimationTime)
+                        + " ms have elapsed since previous animation.");
+            }
+            mLastAnimationTime = start;
+
+            listeners = mOnAnimateListeners;
+        }
+
+        if (listeners != null) {
+            for (int i = 0; i < listeners.length; i++) {
+                listeners[i].onAnimate();
+            }
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "Animation took " + (SystemClock.uptimeMillis() - start) + " ms.");
+        }
+    }
+
     private void doDraw() {
-        if (mDrawScheduled) {
+        final long start;
+        final OnDrawListener[] listeners;
+        synchronized (mLock) {
+            if (!mDrawScheduled) {
+                return; // no work to do
+            }
             mDrawScheduled = false;
 
-            final long start = SystemClock.uptimeMillis();
+            start = SystemClock.uptimeMillis();
             if (DEBUG) {
                 Log.d(TAG, "Performing draw: " + Math.max(0, start - mLastDrawTime)
                         + " ms have elapsed since previous draw.");
             }
             mLastDrawTime = start;
 
-            final OnDrawListener[] listeners = mOnDrawListeners;
-            if (listeners != null) {
-                for (int i = 0; i < listeners.length; i++) {
-                    listeners[i].onDraw();
-                }
-            }
+            listeners = mOnDrawListeners;
+        }
 
-            if (DEBUG) {
-                Log.d(TAG, "Draw took " + (SystemClock.uptimeMillis() - start) + " ms.");
+        if (listeners != null) {
+            for (int i = 0; i < listeners.length; i++) {
+                listeners[i].onDraw();
             }
         }
+
+        if (DEBUG) {
+            Log.d(TAG, "Draw took " + (SystemClock.uptimeMillis() - start) + " ms.");
+        }
     }
 
     /**
      * Adds an animation listener.
-     * Must be called on the UI thread.
      *
      * @param listener The listener to add.
      */
@@ -277,13 +309,14 @@
             Log.d(TAG, "Adding onAnimate listener: " + listener);
         }
 
-        mOnAnimateListeners = ArrayUtils.appendElement(OnAnimateListener.class,
-                mOnAnimateListeners, listener);
+        synchronized (mLock) {
+            mOnAnimateListeners = ArrayUtils.appendElement(OnAnimateListener.class,
+                    mOnAnimateListeners, listener);
+        }
     }
 
     /**
      * Removes an animation listener.
-     * Must be called on the UI thread.
      *
      * @param listener The listener to remove.
      */
@@ -296,14 +329,15 @@
             Log.d(TAG, "Removing onAnimate listener: " + listener);
         }
 
-        mOnAnimateListeners = ArrayUtils.removeElement(OnAnimateListener.class,
-                mOnAnimateListeners, listener);
-        stopTimingLoopIfNoListeners();
+        synchronized (mLock) {
+            mOnAnimateListeners = ArrayUtils.removeElement(OnAnimateListener.class,
+                    mOnAnimateListeners, listener);
+            stopTimingLoopIfNoListenersLocked();
+        }
     }
 
     /**
      * Adds a draw listener.
-     * Must be called on the UI thread.
      *
      * @param listener The listener to add.
      */
@@ -316,8 +350,10 @@
             Log.d(TAG, "Adding onDraw listener: " + listener);
         }
 
-        mOnDrawListeners = ArrayUtils.appendElement(OnDrawListener.class,
-                mOnDrawListeners, listener);
+        synchronized (mLock) {
+            mOnDrawListeners = ArrayUtils.appendElement(OnDrawListener.class,
+                    mOnDrawListeners, listener);
+        }
     }
 
     /**
@@ -335,12 +371,14 @@
             Log.d(TAG, "Removing onDraw listener: " + listener);
         }
 
-        mOnDrawListeners = ArrayUtils.removeElement(OnDrawListener.class,
-                mOnDrawListeners, listener);
-        stopTimingLoopIfNoListeners();
+        synchronized (mLock) {
+            mOnDrawListeners = ArrayUtils.removeElement(OnDrawListener.class,
+                    mOnDrawListeners, listener);
+            stopTimingLoopIfNoListenersLocked();
+        }
     }
 
-    private void stopTimingLoopIfNoListeners() {
+    private void stopTimingLoopIfNoListenersLocked() {
         if (mOnDrawListeners == null && mOnAnimateListeners == null) {
             if (DEBUG) {
                 Log.d(TAG, "Stopping timing loop.");
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index cf4cff9..8d32edc 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2166,15 +2166,13 @@
         float mScaleY = 1f;
 
         /**
-         * The amount of scale in the x direction around the pivot point. A
-         * value of 1 means no scaling is applied.
+         * The x location of the point around which the view is rotated and scaled.
          */
         @ViewDebug.ExportedProperty
         float mPivotX = 0f;
 
         /**
-         * The amount of scale in the y direction around the pivot point. A
-         * value of 1 means no scaling is applied.
+         * The y location of the point around which the view is rotated and scaled.
          */
         @ViewDebug.ExportedProperty
         float mPivotY = 0f;
@@ -7312,6 +7310,7 @@
      * 
      * @return The degrees of rotation.
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     public float getRotation() {
         return mTransformationInfo != null ? mTransformationInfo.mRotation : 0;
     }
@@ -7353,6 +7352,7 @@
      * 
      * @return The degrees of Y rotation.
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     public float getRotationY() {
         return mTransformationInfo != null ? mTransformationInfo.mRotationY : 0;
     }
@@ -7399,6 +7399,7 @@
      * 
      * @return The degrees of X rotation.
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     public float getRotationX() {
         return mTransformationInfo != null ? mTransformationInfo.mRotationX : 0;
     }
@@ -7446,6 +7447,7 @@
      * @see #getPivotY()
      * @return The scaling factor.
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     public float getScaleX() {
         return mTransformationInfo != null ? mTransformationInfo.mScaleX : 1;
     }
@@ -7484,6 +7486,7 @@
      * @see #getPivotY()
      * @return The scaling factor.
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     public float getScaleY() {
         return mTransformationInfo != null ? mTransformationInfo.mScaleY : 1;
     }
@@ -7522,6 +7525,7 @@
      * @see #getPivotY()
      * @return The x location of the pivot point.
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     public float getPivotX() {
         return mTransformationInfo != null ? mTransformationInfo.mPivotX : 0;
     }
@@ -7566,6 +7570,7 @@
      * @see #getPivotY()
      * @return The y location of the pivot point.
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     public float getPivotY() {
         return mTransformationInfo != null ? mTransformationInfo.mPivotY : 0;
     }
@@ -7606,6 +7611,7 @@
      * <p>By default this is 1.0f.
      * @return The opacity of the view.
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     public float getAlpha() {
         return mTransformationInfo != null ? mTransformationInfo.mAlpha : 1;
     }
@@ -7619,6 +7625,10 @@
      * equivalent to calling {@link #setLayerType(int, android.graphics.Paint)} and
      * setting a hardware layer.</p>
      *
+     * <p>Note that setting alpha to a translucent value (0 < alpha < 1) may have
+     * performance implications. It is generally best to use the alpha property sparingly and
+     * transiently, as in the case of fading animations.</p>
+     *
      * @param alpha The opacity of the view.
      *
      * @see #setLayerType(int, android.graphics.Paint)
@@ -7916,6 +7926,7 @@
      *
      * @return The visual x position of this view, in pixels.
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     public float getX() {
         return mLeft + (mTransformationInfo != null ? mTransformationInfo.mTranslationX : 0);
     }
@@ -7938,6 +7949,7 @@
      *
      * @return The visual y position of this view, in pixels.
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     public float getY() {
         return mTop + (mTransformationInfo != null ? mTransformationInfo.mTranslationY : 0);
     }
@@ -7961,6 +7973,7 @@
      *
      * @return The horizontal position of this view relative to its left position, in pixels.
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     public float getTranslationX() {
         return mTransformationInfo != null ? mTransformationInfo.mTranslationX : 0;
     }
@@ -7997,6 +8010,7 @@
      * @return The vertical position of this view relative to its top position,
      * in pixels.
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     public float getTranslationY() {
         return mTransformationInfo != null ? mTransformationInfo.mTranslationY : 0;
     }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 6ec2e8d..75267bb 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -536,7 +536,7 @@
     /**
      * Return the available screen width that we should report for the
      * configuration.  This must be no larger than
-     * {@link #getNonDecorDisplayWidth(int, int)}; it may be smaller than
+     * {@link #getNonDecorDisplayWidth(int, int, int)}; it may be smaller than
      * that to account for more transient decoration like a status bar.
      */
     public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation);
@@ -544,7 +544,7 @@
     /**
      * Return the available screen height that we should report for the
      * configuration.  This must be no larger than
-     * {@link #getNonDecorDisplayHeight(int, int)}; it may be smaller than
+     * {@link #getNonDecorDisplayHeight(int, int, int)}; it may be smaller than
      * that to account for more transient decoration like a status bar.
      */
     public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation);
@@ -754,12 +754,8 @@
      * Called when layout of the windows is finished.  After this function has
      * returned, all windows given to layoutWindow() <em>must</em> have had a
      * frame assigned.
-     *  
-     * @return Return any bit set of {@link #FINISH_LAYOUT_REDO_LAYOUT},
-     * {@link #FINISH_LAYOUT_REDO_CONFIG}, {@link #FINISH_LAYOUT_REDO_WALLPAPER},
-     * or {@link #FINISH_LAYOUT_REDO_ANIM}.
      */
-    public int finishLayoutLw();
+    public void finishLayoutLw();
 
     /** Layout state may have changed (so another layout will be performed) */
     static final int FINISH_LAYOUT_REDO_LAYOUT = 0x0001;
@@ -822,7 +818,7 @@
 
     public interface ScreenOnListener {
         void onScreenOn();
-    };
+    }
 
     /**
      * Called when the power manager would like to turn the screen on.
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 4a42e92..fbafc64 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -367,12 +367,52 @@
     private class WebViewInputConnection extends BaseInputConnection {
         // Used for mapping characters to keys typed.
         private KeyCharacterMap mKeyCharacterMap;
+        private boolean mIsKeySentByMe;
 
         public WebViewInputConnection() {
             super(WebView.this, true);
         }
 
         @Override
+        public boolean sendKeyEvent(KeyEvent event) {
+            // Latin IME occasionally sends delete codes directly using
+            // sendKeyEvents. WebViewInputConnection should treat this
+            // as a deleteSurroundingText.
+            if (!mIsKeySentByMe
+                    && event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
+                Editable editable = getEditable();
+                int selectionStart = Selection.getSelectionStart(editable);
+                int selectionEnd = Selection.getSelectionEnd(editable);
+                if (selectionEnd > 0 && (selectionStart == selectionEnd)) {
+                    int action = event.getAction();
+                    if (action == KeyEvent.ACTION_UP) {
+                        return deleteSurroundingText(1, 0);
+                    } else if (action == KeyEvent.ACTION_DOWN) {
+                        return true; // the delete will happen in ACTION_UP
+                    }
+                }
+            }
+            return super.sendKeyEvent(event);
+        }
+
+        public void setTextAndKeepSelection(CharSequence text) {
+            Editable editable = getEditable();
+            int selectionStart = Selection.getSelectionStart(editable);
+            int selectionEnd = Selection.getSelectionEnd(editable);
+            editable.replace(0, editable.length(), text);
+            InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm != null) {
+                // Since the text has changed, do not allow the IME to replace the
+                // existing text as though it were a completion.
+                imm.restartInput(WebView.this);
+            }
+            // Keep the previous selection.
+            selectionStart = Math.min(selectionStart, editable.length());
+            selectionEnd = Math.min(selectionEnd, editable.length());
+            setSelection(selectionStart, selectionEnd);
+        }
+
+        @Override
         public boolean setComposingText(CharSequence text, int newCursorPosition) {
             Editable editable = getEditable();
             int start = getComposingSpanStart(editable);
@@ -393,7 +433,8 @@
         @Override
         public boolean commitText(CharSequence text, int newCursorPosition) {
             setComposingText(text, newCursorPosition);
-            finishComposingText();
+            int cursorPosition = Selection.getSelectionEnd(getEditable());
+            setComposingRegion(cursorPosition, cursorPosition);
             return true;
         }
 
@@ -417,6 +458,7 @@
          * @param text The new text to replace the changed text.
          */
         private void setNewText(int start, int end, CharSequence text) {
+            mIsKeySentByMe = true;
             Editable editable = getEditable();
             CharSequence original = editable.subSequence(start, end);
             boolean isCharacterAdd = false;
@@ -434,10 +476,8 @@
             }
             if (isCharacterAdd) {
                 sendCharacter(text.charAt(textLength - 1));
-                mTextGeneration++;
             } else if (isCharacterDelete) {
                 sendDeleteKey();
-                mTextGeneration++;
             } else if (textLength != originalLength ||
                     !TextUtils.regionMatches(text, 0, original, 0,
                             textLength)) {
@@ -447,6 +487,7 @@
                         REPLACE_TEXT, start,  end, text.toString());
                 mPrivateHandler.sendMessage(replaceMessage);
             }
+            mIsKeySentByMe = false;
         }
 
         /**
@@ -509,7 +550,7 @@
     private final RectF mVisibleContentRect = new RectF();
     private boolean mGLViewportEmpty = false;
     WebViewInputConnection mInputConnection = null;
-
+    private int mFieldPointer;
 
     /**
      *  Transportation object for returning WebView across thread boundaries.
@@ -803,6 +844,7 @@
     static final int HANDLE_ID_EXTENT = 3;
 
     static boolean sDisableNavcache = false;
+    static boolean sEnableWebTextView = false;
     // the color used to highlight the touch rectangles
     static final int HIGHLIGHT_COLOR = 0x6633b5e5;
     // the region indicating where the user touched on the screen
@@ -1424,7 +1466,6 @@
     private void init() {
         OnTrimMemoryListener.init(getContext());
         sDisableNavcache = nativeDisableNavcache();
-
         setWillNotDraw(false);
         setFocusable(true);
         setFocusableInTouchMode(true);
@@ -2613,7 +2654,7 @@
         checkThread();
         mContentWidth = 0;
         mContentHeight = 0;
-        setBaseLayer(0, null, false, false, false);
+        setBaseLayer(0, null, false, false);
         mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
     }
 
@@ -2781,6 +2822,34 @@
         return result;
     }
 
+    int getBlockLeftEdge(int x, int y, float readingScale) {
+        if (!sDisableNavcache) {
+            return nativeGetBlockLeftEdge(x, y, readingScale);
+        }
+
+        float invReadingScale = 1.0f / readingScale;
+        int readingWidth = (int) (getViewWidth() * invReadingScale);
+        int left = NO_LEFTEDGE;
+        if (mFocusedNode != null) {
+            final int length = mFocusedNode.mEnclosingParentRects.length;
+            for (int i = 0; i < length; i++) {
+                Rect rect = mFocusedNode.mEnclosingParentRects[i];
+                if (rect.width() < mFocusedNode.mHitTestSlop) {
+                    // ignore bounding boxes that are too small
+                    continue;
+                } else if (left != NO_LEFTEDGE && rect.width() > readingWidth) {
+                    // stop when bounding box doesn't fit the screen width
+                    // at reading scale
+                    break;
+                }
+
+                left = rect.left;
+            }
+        }
+
+        return left;
+    }
+
     // Called by JNI when the DOM has changed the focus.  Clear the focus so
     // that new keys will go to the newly focused field
     private void domChangedFocus() {
@@ -4480,6 +4549,8 @@
 
         if (canvas.isHardwareAccelerated()) {
             mZoomManager.setHardwareAccelerated();
+        } else {
+            mWebViewCore.resumeWebKitDraw();
         }
 
         int saveCount = canvas.save();
@@ -4722,11 +4793,19 @@
     }
 
     void setBaseLayer(int layer, Region invalRegion, boolean showVisualIndicator,
-            boolean isPictureAfterFirstLayout, boolean registerPageSwapCallback) {
+            boolean isPictureAfterFirstLayout) {
         if (mNativeClass == 0)
             return;
-        nativeSetBaseLayer(mNativeClass, layer, invalRegion, showVisualIndicator,
-                isPictureAfterFirstLayout, registerPageSwapCallback);
+        boolean queueFull;
+        queueFull = nativeSetBaseLayer(mNativeClass, layer, invalRegion,
+                                       showVisualIndicator, isPictureAfterFirstLayout);
+
+        if (layer == 0 || isPictureAfterFirstLayout) {
+            mWebViewCore.resumeWebKitDraw();
+        } else if (queueFull) {
+            mWebViewCore.pauseWebKitDraw();
+        }
+
         if (mHTML5VideoViewProxy != null) {
             mHTML5VideoViewProxy.setBaseLayer(layer);
         }
@@ -5091,6 +5170,9 @@
      * multiline, and what text it contains.  It also removes it if necessary.
      */
     /* package */ void rebuildWebTextView() {
+        if (!sEnableWebTextView) {
+            return; // always use WebKit's text entry
+        }
         // If the WebView does not have focus, do nothing until it gains focus.
         if (!hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())) {
             return;
@@ -8653,14 +8735,17 @@
                 case UPDATE_TEXTFIELD_TEXT_MSG_ID:
                     // Make sure that the textfield is currently focused
                     // and representing the same node as the pointer.
-                    if (inEditingMode() &&
-                            mWebTextView.isSameTextField(msg.arg1)) {
-                        if (msg.arg2 == mTextGeneration) {
-                            String text = (String) msg.obj;
-                            if (null == text) {
-                                text = "";
-                            }
+                    if (msg.arg2 == mTextGeneration) {
+                        String text = (String) msg.obj;
+                        if (null == text) {
+                            text = "";
+                        }
+                        if (inEditingMode() &&
+                                mWebTextView.isSameTextField(msg.arg1)) {
                             mWebTextView.setTextAndKeepSelection(text);
+                        } else if (mInputConnection != null &&
+                                mFieldPointer == msg.arg1) {
+                            mInputConnection.setTextAndKeepSelection(text);
                         }
                     }
                     break;
@@ -8951,15 +9036,8 @@
                 case INIT_EDIT_FIELD:
                     if (mInputConnection != null) {
                         mTextGeneration = 0;
-                        String text = (String)msg.obj;
-                        mInputConnection.beginBatchEdit();
-                        Editable editable = mInputConnection.getEditable();
-                        editable.replace(0, editable.length(), text);
-                        int start = msg.arg1;
-                        int end = msg.arg2;
-                        mInputConnection.setComposingRegion(end, end);
-                        mInputConnection.setSelection(start, end);
-                        mInputConnection.endBatchEdit();
+                        mFieldPointer = msg.arg1;
+                        mInputConnection.setTextAndKeepSelection((String) msg.obj);
                     }
                     break;
 
@@ -9009,6 +9087,7 @@
     /** @hide Called by JNI when pages are swapped (only occurs with hardware
      * acceleration) */
     protected void pageSwapCallback(boolean notifyAnimationStarted) {
+        mWebViewCore.resumeWebKitDraw();
         if (inEditingMode()) {
             didUpdateWebTextViewDimensions(ANYWHERE);
         }
@@ -9031,13 +9110,9 @@
         boolean isPictureAfterFirstLayout = viewState != null;
 
         if (updateBaseLayer) {
-            // Request a callback on pageSwap (to reposition the webtextview)
-            boolean registerPageSwapCallback =
-                !mZoomManager.isFixedLengthAnimationInProgress() && inEditingMode();
-
             setBaseLayer(draw.mBaseLayer, draw.mInvalRegion,
                     getSettings().getShowVisualIndicator(),
-                    isPictureAfterFirstLayout, registerPageSwapCallback);
+                    isPictureAfterFirstLayout);
         }
         final Point viewSize = draw.mViewSize;
         // We update the layout (i.e. request a layout from the
@@ -9102,7 +9177,7 @@
             if (inEditingMode()
                     && mWebTextView.isSameTextField(nodePointer)) {
                 mWebTextView.setSelectionFromWebKit(data.mStart, data.mEnd);
-            } else if (mInputConnection != null){
+            } else if (mInputConnection != null && mFieldPointer == nodePointer) {
                 mInputConnection.setSelection(data.mStart, data.mEnd);
             }
         }
@@ -9715,11 +9790,6 @@
         }
     }
 
-    /** @hide call pageSwapCallback upon next page swap */
-    protected void registerPageSwapCallback() {
-        nativeRegisterPageSwapCallback(mNativeClass);
-    }
-
     /** @hide discard all textures from tiles */
     protected void discardAllTextures() {
         nativeDiscardAllTextures();
@@ -9882,10 +9952,9 @@
     private native void     nativeSetFindIsEmpty();
     private native void     nativeSetFindIsUp(boolean isUp);
     private native void     nativeSetHeightCanMeasure(boolean measure);
-    private native void     nativeSetBaseLayer(int nativeInstance,
+    private native boolean  nativeSetBaseLayer(int nativeInstance,
             int layer, Region invalRegion,
-            boolean showVisualIndicator, boolean isPictureAfterFirstLayout,
-            boolean registerPageSwapCallback);
+            boolean showVisualIndicator, boolean isPictureAfterFirstLayout);
     private native int      nativeGetBaseLayer();
     private native void     nativeShowCursorTimed();
     private native void     nativeReplaceBaseContent(int content);
@@ -9897,7 +9966,6 @@
     private native void     nativeStopGL();
     private native Rect     nativeSubtractLayers(Rect content);
     private native int      nativeTextGeneration();
-    private native void     nativeRegisterPageSwapCallback(int nativeInstance);
     private native void     nativeDiscardAllTextures();
     private native void     nativeTileProfilingStart();
     private native float    nativeTileProfilingStop();
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 395a638..8a9c12d 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -872,6 +872,7 @@
         Rect[] mTouchRects;
         boolean mEditable;
         int mTapHighlightColor = WebView.HIGHLIGHT_COLOR;
+        Rect[] mEnclosingParentRects;
 
         // These are the input values that produced this hit test
         int mHitTestX;
@@ -2169,7 +2170,36 @@
                 .obtainMessage(WebView.INVAL_RECT_MSG_ID));
     }
 
+    private Boolean m_skipDrawFlag = false;
+    private boolean m_drawWasSkipped = false;
+
+    void pauseWebKitDraw() {
+        synchronized (m_skipDrawFlag) {
+            if (!m_skipDrawFlag) {
+                m_skipDrawFlag = true;
+            }
+        }
+    }
+
+    void resumeWebKitDraw() {
+        synchronized (m_skipDrawFlag) {
+            if (m_skipDrawFlag && m_drawWasSkipped) {
+                // a draw was dropped, send a retry
+                m_drawWasSkipped = false;
+                mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
+            }
+            m_skipDrawFlag = false;
+        }
+    }
+
     private void webkitDraw() {
+        synchronized (m_skipDrawFlag) {
+            if (m_skipDrawFlag) {
+                m_drawWasSkipped = true;
+                return;
+            }
+        }
+
         mDrawIsScheduled = false;
         DrawData draw = new DrawData();
         if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
@@ -2178,7 +2208,7 @@
         if (draw.mBaseLayer == 0) {
             if (mWebView != null && !mWebView.isPaused()) {
                 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, resending draw message");
-                mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
+                mEventHub.sendMessageDelayed(Message.obtain(null, EventHub.WEBKIT_DRAW), 10);
             } else {
                 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, webview paused");
             }
@@ -2746,12 +2776,16 @@
     }
 
     // called by JNI
-    private void initEditField(String text, int start, int end) {
+    private void initEditField(int pointer, String text, int start, int end) {
         if (mWebView == null) {
             return;
         }
         Message.obtain(mWebView.mPrivateHandler,
-                WebView.INIT_EDIT_FIELD, start, end, text).sendToTarget();
+                WebView.INIT_EDIT_FIELD, pointer, 0, text).sendToTarget();
+        Message.obtain(mWebView.mPrivateHandler,
+                WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
+                0, new TextSelectionData(start, end, 0))
+                .sendToTarget();
     }
 
     private native void nativeUpdateFrameCacheIfLoading(int nativeClass);
@@ -2789,17 +2823,6 @@
     }
 
     // called by JNI
-    private void requestKeyboardWithSelection(int pointer, int selStart,
-            int selEnd, int textGeneration) {
-        if (mWebView != null) {
-            Message.obtain(mWebView.mPrivateHandler,
-                    WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
-                    textGeneration, new TextSelectionData(selStart, selEnd, 0))
-                    .sendToTarget();
-        }
-    }
-
-    // called by JNI
     private void requestKeyboard(boolean showKeyboard) {
         if (mWebView != null) {
             Message.obtain(mWebView.mPrivateHandler,
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 8ffba64..ae2d617 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -717,7 +717,7 @@
     private void zoomToReadingLevel() {
         final float readingScale = getReadingLevelScale();
 
-        int left = mWebView.nativeGetBlockLeftEdge(mAnchorX, mAnchorY, mActualScale);
+        int left = mWebView.getBlockLeftEdge(mAnchorX, mAnchorY, readingScale);
         if (left != WebView.NO_LEFTEDGE) {
             // add a 5pt padding to the left edge.
             int viewLeft = mWebView.contentToViewX(left < 5 ? 0 : (left - 5))
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 1592061..62afd61 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -427,13 +427,22 @@
 
         public SetOnClickPendingIntent(Parcel parcel) {
             viewId = parcel.readInt();
-            pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
+
+            // We check a flag to determine if the parcel contains a PendingIntent.
+            if (parcel.readInt() != 0) {
+                pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
+            }
         }
 
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(TAG);
             dest.writeInt(viewId);
-            pendingIntent.writeToParcel(dest, 0 /* no flags */);
+
+            // We use a flag to indicate whether the parcel contains a valid object.
+            dest.writeInt(pendingIntent != null ? 1 : 0);
+            if (pendingIntent != null) {
+                pendingIntent.writeToParcel(dest, 0 /* no flags */);
+            }
         }
 
         @Override
@@ -445,35 +454,39 @@
             // sense, do they mean to set a PendingIntent template for the AdapterView's children?
             if (mIsWidgetCollectionChild) {
                 Log.e("RemoteViews", "Cannot setOnClickPendingIntent for collection item " +
-				"(id: " + viewId + ")");
+                        "(id: " + viewId + ")");
                 // TODO: return; We'll let this slide until apps are up to date.
             }
 
-            if (target != null && pendingIntent != null) {
-                OnClickListener listener = new OnClickListener() {
-                    public void onClick(View v) {
-                        // Find target view location in screen coordinates and
-                        // fill into PendingIntent before sending.
-                        final float appScale = v.getContext().getResources()
-                                .getCompatibilityInfo().applicationScale;
-                        final int[] pos = new int[2];
-                        v.getLocationOnScreen(pos);
-
-                        final Rect rect = new Rect();
-                        rect.left = (int) (pos[0] * appScale + 0.5f);
-                        rect.top = (int) (pos[1] * appScale + 0.5f);
-                        rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
-                        rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
-
-                        final Intent intent = new Intent();
-                        intent.setSourceBounds(rect);
-                        startIntentSafely(v.getContext(), pendingIntent, intent);
-                    }
-                };
+            if (target != null) {
+                // If the pendingIntent is null, we clear the onClickListener
+                OnClickListener listener = null;
+                if (pendingIntent != null) {
+                    listener = new OnClickListener() {
+                        public void onClick(View v) {
+                            // Find target view location in screen coordinates and
+                            // fill into PendingIntent before sending.
+                            final float appScale = v.getContext().getResources()
+                                    .getCompatibilityInfo().applicationScale;
+                            final int[] pos = new int[2];
+                            v.getLocationOnScreen(pos);
+    
+                            final Rect rect = new Rect();
+                            rect.left = (int) (pos[0] * appScale + 0.5f);
+                            rect.top = (int) (pos[1] * appScale + 0.5f);
+                            rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
+                            rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
+    
+                            final Intent intent = new Intent();
+                            intent.setSourceBounds(rect);
+                            startIntentSafely(v.getContext(), pendingIntent, intent);
+                        }
+                    };
+                }
                 target.setOnClickListener(listener);
             }
         }
-        
+
         int viewId;
         PendingIntent pendingIntent;
 
@@ -667,6 +680,9 @@
                 Log.d("RemoteViews", "read viewId=0x" + Integer.toHexString(this.viewId)
                         + " methodName=" + this.methodName + " type=" + this.type);
             }
+
+            // For some values that may have been null, we first check a flag to see if they were
+            // written to the parcel.
             switch (this.type) {
                 case BOOLEAN:
                     this.value = in.readInt() != 0;
@@ -699,16 +715,22 @@
                     this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
                     break;
                 case URI:
-                    this.value = Uri.CREATOR.createFromParcel(in);
+                    if (in.readInt() != 0) {
+                        this.value = Uri.CREATOR.createFromParcel(in);
+                    }
                     break;
                 case BITMAP:
-                    this.value = Bitmap.CREATOR.createFromParcel(in);
+                    if (in.readInt() != 0) {
+                        this.value = Bitmap.CREATOR.createFromParcel(in);
+                    }
                     break;
                 case BUNDLE:
                     this.value = in.readBundle();
                     break;
                 case INTENT:
-                    this.value = Intent.CREATOR.createFromParcel(in);
+                    if (in.readInt() != 0) {
+                        this.value = Intent.CREATOR.createFromParcel(in);
+                    }
                     break;
                 default:
                     break;
@@ -725,6 +747,9 @@
                 Log.d("RemoteViews", "write viewId=0x" + Integer.toHexString(this.viewId)
                         + " methodName=" + this.methodName + " type=" + this.type);
             }
+
+            // For some values which are null, we record an integer flag to indicate whether
+            // we have written a valid value to the parcel.
             switch (this.type) {
                 case BOOLEAN:
                     out.writeInt((Boolean) this.value ? 1 : 0);
@@ -757,16 +782,25 @@
                     TextUtils.writeToParcel((CharSequence)this.value, out, flags);   
                     break;
                 case URI:
-                    ((Uri)this.value).writeToParcel(out, flags);
+                    out.writeInt(this.value != null ? 1 : 0);
+                    if (this.value != null) {
+                        ((Uri)this.value).writeToParcel(out, flags);
+                    }
                     break;
                 case BITMAP:
-                    ((Bitmap)this.value).writeToParcel(out, flags);
+                    out.writeInt(this.value != null ? 1 : 0);
+                    if (this.value != null) {
+                        ((Bitmap)this.value).writeToParcel(out, flags);
+                    }
                     break;
                 case BUNDLE:
                     out.writeBundle((Bundle) this.value);
                     break;
                 case INTENT:
-                    ((Intent)this.value).writeToParcel(out, flags);
+                    out.writeInt(this.value != null ? 1 : 0);
+                    if (this.value != null) {
+                        ((Intent)this.value).writeToParcel(out, flags);
+                    }
                     break;
                 default:
                     break;
diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
index ebd355a..d51ced11 100644
--- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
@@ -653,6 +653,7 @@
 
             case MotionEvent.ACTION_CANCEL:
                 handleMove(event);
+                handleCancel(event);
                 handled = true;
                 break;
         }
@@ -678,6 +679,12 @@
         if (DEBUG && mDragging) Log.v(TAG, "** Handle RELEASE");
         switchToState(STATE_FINISH, event.getX(), event.getY());
     }
+    
+    private void handleCancel(MotionEvent event) {
+        if (DEBUG && mDragging) Log.v(TAG, "** Handle CANCEL");
+        mActiveTarget = -1; // Drop the active target if canceled.
+        switchToState(STATE_FINISH, event.getX(), event.getY());
+    }
 
     private void handleMove(MotionEvent event) {
         if (!mDragging) {
diff --git a/core/java/com/google/android/mms/pdu/PduPersister.java b/core/java/com/google/android/mms/pdu/PduPersister.java
index c4be513..8d57e5d 100644
--- a/core/java/com/google/android/mms/pdu/PduPersister.java
+++ b/core/java/com/google/android/mms/pdu/PduPersister.java
@@ -510,13 +510,26 @@
      * @throws MmsException Failed to load some fields of a PDU.
      */
     public GenericPdu load(Uri uri) throws MmsException {
-        PduCacheEntry cacheEntry = PDU_CACHE_INSTANCE.get(uri);
-        if (cacheEntry != null) {
-            return cacheEntry.getPdu();
+        PduCacheEntry cacheEntry;
+        synchronized(PDU_CACHE_INSTANCE) {
+            if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
+                try {
+                    PDU_CACHE_INSTANCE.wait();
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "load: ", e);
+                }
+                cacheEntry = PDU_CACHE_INSTANCE.get(uri);
+                if (cacheEntry != null) {
+                    return cacheEntry.getPdu();
+                }
+            }
+            // Tell the cache to indicate to other callers that this item
+            // is currently being updated.
+            PDU_CACHE_INSTANCE.setUpdating(uri, true);
         }
 
         Cursor c = SqliteWrapper.query(mContext, mContentResolver, uri,
-                        PDU_PROJECTION, null, null, null);
+                                                PDU_PROJECTION, null, null, null);
         PduHeaders headers = new PduHeaders();
         Set<Entry<Integer, Integer>> set;
         long msgId = ContentUris.parseId(uri);
@@ -634,9 +647,14 @@
                         "Unrecognized PDU type: " + Integer.toHexString(msgType));
         }
 
-        cacheEntry = new PduCacheEntry(pdu, msgBox, threadId);
-        PDU_CACHE_INSTANCE.put(uri, cacheEntry);
-        return pdu;
+        synchronized(PDU_CACHE_INSTANCE ) {
+            assert(PDU_CACHE_INSTANCE.get(uri) == null);
+            // Update the cache entry with the real info
+            cacheEntry = new PduCacheEntry(pdu, msgBox, threadId);
+            PDU_CACHE_INSTANCE.put(uri, cacheEntry);
+            PDU_CACHE_INSTANCE.notifyAll();     // tell anybody waiting on this entry to go ahead
+            return pdu;
+        }
     }
 
     private void persistAddress(
@@ -818,6 +836,17 @@
      * @throws MmsException Bad URI or updating failed.
      */
     public void updateHeaders(Uri uri, SendReq sendReq) {
+        synchronized(PDU_CACHE_INSTANCE) {
+            // If the cache item is getting updated, wait until it's done updating before
+            // purging it.
+            if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
+                try {
+                    PDU_CACHE_INSTANCE.wait();
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "updateHeaders: ", e);
+                }
+            }
+        }
         PDU_CACHE_INSTANCE.purge(uri);
 
         ContentValues values = new ContentValues(10);
@@ -969,52 +998,72 @@
      */
     public void updateParts(Uri uri, PduBody body)
             throws MmsException {
-        PduCacheEntry cacheEntry = PDU_CACHE_INSTANCE.get(uri);
-        if (cacheEntry != null) {
-            ((MultimediaMessagePdu) cacheEntry.getPdu()).setBody(body);
-        }
-
-        ArrayList<PduPart> toBeCreated = new ArrayList<PduPart>();
-        HashMap<Uri, PduPart> toBeUpdated = new HashMap<Uri, PduPart>();
-
-        int partsNum = body.getPartsNum();
-        StringBuilder filter = new StringBuilder().append('(');
-        for (int i = 0; i < partsNum; i++) {
-            PduPart part = body.getPart(i);
-            Uri partUri = part.getDataUri();
-            if ((partUri == null) || !partUri.getAuthority().startsWith("mms")) {
-                toBeCreated.add(part);
-            } else {
-                toBeUpdated.put(partUri, part);
-
-                // Don't use 'i > 0' to determine whether we should append
-                // 'AND' since 'i = 0' may be skipped in another branch.
-                if (filter.length() > 1) {
-                    filter.append(" AND ");
+        try {
+            PduCacheEntry cacheEntry;
+            synchronized(PDU_CACHE_INSTANCE) {
+                if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
+                    try {
+                        PDU_CACHE_INSTANCE.wait();
+                    } catch (InterruptedException e) {
+                        Log.e(TAG, "updateParts: ", e);
+                    }
+                    cacheEntry = PDU_CACHE_INSTANCE.get(uri);
+                    if (cacheEntry != null) {
+                        ((MultimediaMessagePdu) cacheEntry.getPdu()).setBody(body);
+                    }
                 }
-
-                filter.append(Part._ID);
-                filter.append("!=");
-                DatabaseUtils.appendEscapedSQLString(filter, partUri.getLastPathSegment());
+                // Tell the cache to indicate to other callers that this item
+                // is currently being updated.
+                PDU_CACHE_INSTANCE.setUpdating(uri, true);
             }
-        }
-        filter.append(')');
 
-        long msgId = ContentUris.parseId(uri);
+            ArrayList<PduPart> toBeCreated = new ArrayList<PduPart>();
+            HashMap<Uri, PduPart> toBeUpdated = new HashMap<Uri, PduPart>();
 
-        // Remove the parts which doesn't exist anymore.
-        SqliteWrapper.delete(mContext, mContentResolver,
-                Uri.parse(Mms.CONTENT_URI + "/" + msgId + "/part"),
-                filter.length() > 2 ? filter.toString() : null, null);
+            int partsNum = body.getPartsNum();
+            StringBuilder filter = new StringBuilder().append('(');
+            for (int i = 0; i < partsNum; i++) {
+                PduPart part = body.getPart(i);
+                Uri partUri = part.getDataUri();
+                if ((partUri == null) || !partUri.getAuthority().startsWith("mms")) {
+                    toBeCreated.add(part);
+                } else {
+                    toBeUpdated.put(partUri, part);
 
-        // Create new parts which didn't exist before.
-        for (PduPart part : toBeCreated) {
-            persistPart(part, msgId);
-        }
+                    // Don't use 'i > 0' to determine whether we should append
+                    // 'AND' since 'i = 0' may be skipped in another branch.
+                    if (filter.length() > 1) {
+                        filter.append(" AND ");
+                    }
 
-        // Update the modified parts.
-        for (Map.Entry<Uri, PduPart> e : toBeUpdated.entrySet()) {
-            updatePart(e.getKey(), e.getValue());
+                    filter.append(Part._ID);
+                    filter.append("!=");
+                    DatabaseUtils.appendEscapedSQLString(filter, partUri.getLastPathSegment());
+                }
+            }
+            filter.append(')');
+
+            long msgId = ContentUris.parseId(uri);
+
+            // Remove the parts which doesn't exist anymore.
+            SqliteWrapper.delete(mContext, mContentResolver,
+                    Uri.parse(Mms.CONTENT_URI + "/" + msgId + "/part"),
+                    filter.length() > 2 ? filter.toString() : null, null);
+
+            // Create new parts which didn't exist before.
+            for (PduPart part : toBeCreated) {
+                persistPart(part, msgId);
+            }
+
+            // Update the modified parts.
+            for (Map.Entry<Uri, PduPart> e : toBeUpdated.entrySet()) {
+                updatePart(e.getKey(), e.getValue());
+            }
+        } finally {
+            synchronized(PDU_CACHE_INSTANCE) {
+                PDU_CACHE_INSTANCE.setUpdating(uri, false);
+                PDU_CACHE_INSTANCE.notifyAll();
+            }
         }
     }
 
@@ -1029,15 +1078,32 @@
         if (uri == null) {
             throw new MmsException("Uri may not be null.");
         }
+        long msgId = -1;
+        try {
+            msgId = ContentUris.parseId(uri);
+        } catch (NumberFormatException e) {
+            // the uri ends with "inbox" or something else like that
+        }
+        boolean existingUri = msgId != -1;
 
-        Integer msgBox = MESSAGE_BOX_MAP.get(uri);
-        if (msgBox == null) {
+        if (!existingUri && MESSAGE_BOX_MAP.get(uri) == null) {
             throw new MmsException(
                     "Bad destination, must be one of "
                     + "content://mms/inbox, content://mms/sent, "
                     + "content://mms/drafts, content://mms/outbox, "
                     + "content://mms/temp.");
         }
+        synchronized(PDU_CACHE_INSTANCE) {
+            // If the cache item is getting updated, wait until it's done updating before
+            // purging it.
+            if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
+                try {
+                    PDU_CACHE_INSTANCE.wait();
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "persist1: ", e);
+                }
+            }
+        }
         PDU_CACHE_INSTANCE.purge(uri);
 
         PduHeaders header = pdu.getPduHeaders();
@@ -1145,14 +1211,20 @@
             }
         }
 
-        Uri res = SqliteWrapper.insert(mContext, mContentResolver, uri, values);
-        if (res == null) {
-            throw new MmsException("persist() failed: return null.");
+        Uri res = null;
+        if (existingUri) {
+            res = uri;
+            SqliteWrapper.update(mContext, mContentResolver, res, values, null, null);
+        } else {
+            res = SqliteWrapper.insert(mContext, mContentResolver, uri, values);
+            if (res == null) {
+                throw new MmsException("persist() failed: return null.");
+            }
+            // Get the real ID of the PDU and update all parts which were
+            // saved with the dummy ID.
+            msgId = ContentUris.parseId(res);
         }
 
-        // Get the real ID of the PDU and update all parts which were
-        // saved with the dummy ID.
-        long msgId = ContentUris.parseId(res);
         values = new ContentValues(1);
         values.put(Part.MSG_ID, msgId);
         SqliteWrapper.update(mContext, mContentResolver,
@@ -1163,7 +1235,9 @@
         // persisted PDU is '8', we should return "content://mms/inbox/8"
         // instead of "content://mms/8".
         // FIXME: Should the MmsProvider be responsible for this???
-        res = Uri.parse(uri + "/" + msgId);
+        if (!existingUri) {
+            res = Uri.parse(uri + "/" + msgId);
+        }
 
         // Save address information.
         for (int addrType : ADDRESS_FIELDS) {
diff --git a/core/java/com/google/android/mms/util/PduCache.java b/core/java/com/google/android/mms/util/PduCache.java
index 059af72..87cb48e 100644
--- a/core/java/com/google/android/mms/util/PduCache.java
+++ b/core/java/com/google/android/mms/util/PduCache.java
@@ -73,10 +73,12 @@
 
     private final HashMap<Integer, HashSet<Uri>> mMessageBoxes;
     private final HashMap<Long, HashSet<Uri>> mThreads;
+    private final HashSet<Uri> mUpdating;
 
     private PduCache() {
         mMessageBoxes = new HashMap<Integer, HashSet<Uri>>();
         mThreads = new HashMap<Long, HashSet<Uri>>();
+        mUpdating = new HashSet<Uri>();
     }
 
     synchronized public static final PduCache getInstance() {
@@ -111,9 +113,22 @@
             msgBox.add(finalKey);
             thread.add(finalKey);
         }
+        setUpdating(uri, false);
         return result;
     }
 
+    synchronized public void setUpdating(Uri uri, boolean updating) {
+        if (updating) {
+            mUpdating.add(uri);
+        } else {
+            mUpdating.remove(uri);
+        }
+    }
+
+    synchronized public boolean isUpdating(Uri uri) {
+        return mUpdating.contains(uri);
+    }
+
     @Override
     synchronized public PduCacheEntry purge(Uri uri) {
         int match = URI_MATCHER.match(uri);
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 23ee8f8..ef6af74 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -67,7 +67,7 @@
     static void freeCaches(JNIEnv* env, jobject) {
         // these are called in no particular order
         SkImageRef_GlobalPool::SetRAMUsed(0);
-        SkGraphics::SetFontCacheUsed(0);
+        SkGraphics::PurgeFontCache();
     }
     
     static jboolean isOpaque(JNIEnv* env, jobject jcanvas) {
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 47ffd94..a1d41ee 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -518,12 +518,6 @@
 bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
     JNIEnv* env = vm2env(fVM);
 
-    // If allocating in the Java heap, only allow a single object to be
-    // allocated for the lifetime of this object.
-    if (fStorageObj != NULL) {
-        SkDebugf("WARNING: One-shot allocator has already allocated (alloc count = %d)\n", fAllocCount);
-//        sk_throw();
-    }
     fStorageObj = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
     fAllocCount += 1;
     return fStorageObj != NULL;
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index c797a73..7f5d54d 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -29,7 +29,10 @@
 namespace android {
 
 //--------------------------------------------------------------------------------------------------
-#define TYPEFACE_ARABIC "/system/fonts/DroidNaskh-Regular.ttf"
+// Using DroidSansArabic for shaping Arabic with Harfbuzz because its metrics are more compatible
+// with the "Roboto" metrics (compared to DroidNaskh-Regular). When we will have an Arabic font
+// whose metrics are similar to the Roboto ones, then we will need to use it for shaping.
+#define TYPEFACE_ARABIC "/system/fonts/DroidSansArabic.ttf"
 #define TYPE_FACE_HEBREW_REGULAR "/system/fonts/DroidSansHebrew-Regular.ttf"
 #define TYPE_FACE_HEBREW_BOLD "/system/fonts/DroidSansHebrew-Bold.ttf"
 #define TYPEFACE_BENGALI "/system/fonts/Lohit-Bengali.ttf"
@@ -37,6 +40,8 @@
 
 ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutEngine);
 
+static KeyedVector<UChar, UChar> gBidiMirrored;
+
 //--------------------------------------------------------------------------------------------------
 
 TextLayoutCache::TextLayoutCache(TextLayoutShaper* shaper) :
@@ -350,6 +355,23 @@
 
     mShaperItem.font = &mFontRec;
     mShaperItem.font->userData = &mShapingPaint;
+
+    // Fill the BiDi mirrored chars map
+    // See: http://www.unicode.org/Public/6.0.0/ucd/extracted/DerivedBinaryProperties.txt
+    gBidiMirrored.add('(', ')');
+    gBidiMirrored.add(')', '(');
+    gBidiMirrored.add('[', ']');
+    gBidiMirrored.add(']', '[');
+    gBidiMirrored.add('{', '}');
+    gBidiMirrored.add('}', '{');
+    gBidiMirrored.add('<', '>');
+    gBidiMirrored.add('>', '<');
+    gBidiMirrored.add(0x00ab, 0x00bb); // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+    gBidiMirrored.add(0x00bb, 0x00ab); // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+    gBidiMirrored.add(0x2039, 0x203a); // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+    gBidiMirrored.add(0x203a, 0x2039); // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+    gBidiMirrored.add(0x2264, 0x2265); // LESS-THAN OR EQUAL TO
+    gBidiMirrored.add(0x2265, 0x2264); // GREATER-THAN OR EQUAL TO
 }
 
 TextLayoutShaper::~TextLayoutShaper() {
@@ -577,6 +599,31 @@
         }
     }
 
+    // Reverse "BiDi mirrored chars" in RTL mode only
+    // See: http://www.unicode.org/Public/6.0.0/ucd/extracted/DerivedBinaryProperties.txt
+    // This is a workaround because Harfbuzz is not able to do mirroring in all cases and
+    // script-run splitting with Harfbuzz is splitting on parenthesis
+    if (isRTL) {
+        for (ssize_t i = 0; i < ssize_t(count); i++) {
+            UChar ch = chars[i];
+            ssize_t index = gBidiMirrored.indexOfKey(ch);
+            // Skip non "BiDi mirrored" chars
+            if (index < 0) {
+                continue;
+            }
+            if (!useNormalizedString) {
+                useNormalizedString = true;
+                mNormalizedString.setTo(false /* not terminated*/, chars, count);
+            }
+            UChar result = gBidiMirrored.valueAt(index);
+            mNormalizedString.setCharAt(i, result);
+#if DEBUG_GLYPHS
+            ALOGD("Rewriting codepoint '%d' to '%d' at position %d",
+                    ch, mNormalizedString[i], int(i));
+#endif
+        }
+    }
+
 #if DEBUG_GLYPHS
     if (useNormalizedString) {
         ALOGD("Will use normalized string '%s', length = %d",
diff --git a/core/jni/android_media_ToneGenerator.cpp b/core/jni/android_media_ToneGenerator.cpp
index 53a0501..26e82aa 100644
--- a/core/jni/android_media_ToneGenerator.cpp
+++ b/core/jni/android_media_ToneGenerator.cpp
@@ -48,7 +48,7 @@
         return false;
     }
 
-    return lpToneGen->startTone(toneType, durationMs);
+    return lpToneGen->startTone((ToneGenerator::tone_type) toneType, durationMs);
 }
 
 static void android_media_ToneGenerator_stopTone(JNIEnv *env, jobject thiz) {
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 990a617..e00970a 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -739,6 +739,11 @@
     return IPCThreadState::self()->getCallingUid();
 }
 
+static jint android_os_Binder_getOrigCallingUid(JNIEnv* env, jobject clazz)
+{
+    return IPCThreadState::self()->getOrigCallingUid();
+}
+
 static jlong android_os_Binder_clearCallingIdentity(JNIEnv* env, jobject clazz)
 {
     return IPCThreadState::self()->clearCallingIdentity();
@@ -810,6 +815,7 @@
      /* name, signature, funcPtr */
     { "getCallingPid", "()I", (void*)android_os_Binder_getCallingPid },
     { "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
+    { "getOrigCallingUidNative", "()I", (void*)android_os_Binder_getOrigCallingUid },
     { "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
     { "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
     { "setThreadStrictModePolicy", "(I)V", (void*)android_os_Binder_setThreadStrictModePolicy },
diff --git a/core/res/lint.xml b/core/res/lint.xml
new file mode 100644
index 0000000..822cdd7
--- /dev/null
+++ b/core/res/lint.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lint>
+    <!-- temporary until we add frameworks support -->
+    <issue id="UnusedResources" severity="ignore" />
+
+    <issue id="PrivateResource" severity="ignore" />
+</lint>
\ No newline at end of file
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 1068b66..56af9aa 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -785,7 +785,7 @@
   </plurals>
   <plurals name="num_minutes_ago">
     <item quantity="one" msgid="3306787433088810191">"iminithi elingu-1 edlule"</item>
-    <item quantity="other" msgid="2176942008915455116">"amaminithi angu-<xliff:g id="COUNT">%d</xliff:g> adlule.."</item>
+    <item quantity="other" msgid="2176942008915455116">"<xliff:g id="COUNT">%d</xliff:g> amaminithi adlule.."</item>
   </plurals>
   <plurals name="num_hours_ago">
     <item quantity="one" msgid="9150797944610821849">"ihora elingu-1 elidlule"</item>
@@ -822,7 +822,7 @@
   </plurals>
   <plurals name="abbrev_num_minutes_ago">
     <item quantity="one" msgid="6361490147113871545">"iminithi elingu-1 edlule"</item>
-    <item quantity="other" msgid="851164968597150710">"amaminithi angu-<xliff:g id="COUNT">%d</xliff:g> adlule"</item>
+    <item quantity="other" msgid="851164968597150710">"<xliff:g id="COUNT">%d</xliff:g> amaminithi adlule"</item>
   </plurals>
   <plurals name="abbrev_num_hours_ago">
     <item quantity="one" msgid="4796212039724722116">"ihora elingu-1 elidlule"</item>
@@ -1111,7 +1111,7 @@
     <string name="no_matches" msgid="8129421908915840737">"Akukho okufanayo"</string>
     <string name="find_on_page" msgid="1946799233822820384">"Thola ekhasini"</string>
   <plurals name="matches_found">
-    <item quantity="one" msgid="8167147081136579439">"okufanayo okungu-1"</item>
+    <item quantity="one" msgid="8167147081136579439">"1 okufanayo"</item>
     <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> ku-<xliff:g id="TOTAL">%d</xliff:g>"</item>
   </plurals>
     <string name="action_mode_done" msgid="7217581640461922289">"Kwenziwe"</string>
diff --git a/core/tests/coretests/src/android/app/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
index 4b1f9fd..d527c0d 100644
--- a/core/tests/coretests/src/android/app/activity/BroadcastTest.java
+++ b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
@@ -303,7 +303,8 @@
     public void testSetSticky() throws Exception {
         Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
         intent.putExtra("test", LaunchpadActivity.DATA_1);
-        ActivityManagerNative.getDefault().unbroadcastIntent(null, intent);
+        ActivityManagerNative.getDefault().unbroadcastIntent(null, intent,
+                Binder.getOrigCallingUser());
 
         ActivityManagerNative.broadcastStickyIntent(intent, null);
         addIntermediate("finished-broadcast");
@@ -320,7 +321,8 @@
         ActivityManagerNative.broadcastStickyIntent(intent, null);
 
         ActivityManagerNative.getDefault().unbroadcastIntent(
-                null, new Intent(LaunchpadActivity.BROADCAST_STICKY1, null));
+                null, new Intent(LaunchpadActivity.BROADCAST_STICKY1, null),
+                Binder.getOrigCallingUser());
         addIntermediate("finished-unbroadcast");
 
         IntentFilter filter = new IntentFilter(LaunchpadActivity.BROADCAST_STICKY1);
diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
index 1df763a..b181122 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
@@ -24,6 +24,8 @@
 import static android.net.NetworkStatsHistory.DataStreamUtils.readVarLong;
 import static android.net.NetworkStatsHistory.DataStreamUtils.writeVarLong;
 import static android.net.NetworkStatsHistory.Entry.UNKNOWN;
+import static android.net.TrafficStats.GB_IN_BYTES;
+import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -50,10 +52,6 @@
 
     private static final long TEST_START = 1194220800000L;
 
-    private static final long KB_IN_BYTES = 1024;
-    private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
-    private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
-
     private NetworkStatsHistory stats;
 
     @Override
diff --git a/data/fonts/DroidSansArabic.ttf b/data/fonts/DroidSansArabic.ttf
new file mode 100644
index 0000000..bdefaac
--- /dev/null
+++ b/data/fonts/DroidSansArabic.ttf
Binary files differ
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 47cdae7..51b07e4 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -25,7 +25,7 @@
 <familyset>
     <family>
         <fileset>
-            <file>DroidNaskh-Regular.ttf</file>
+            <file>DroidSansArabic.ttf</file>
         </fileset>
     </family>
     <family>
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index 9dca329..6a9ed53 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -21,6 +21,7 @@
     frameworks/base/data/fonts/Roboto-Bold.ttf:system/fonts/Roboto-Bold.ttf \
     frameworks/base/data/fonts/Roboto-Italic.ttf:system/fonts/Roboto-Italic.ttf \
     frameworks/base/data/fonts/Roboto-BoldItalic.ttf:system/fonts/Roboto-BoldItalic.ttf \
+    frameworks/base/data/fonts/DroidSansArabic.ttf:system/fonts/DroidSansArabic.ttf \
     frameworks/base/data/fonts/DroidNaskh-Regular.ttf:system/fonts/DroidNaskh-Regular.ttf \
     frameworks/base/data/fonts/DroidSansHebrew-Regular.ttf:system/fonts/DroidSansHebrew-Regular.ttf \
     frameworks/base/data/fonts/DroidSansHebrew-Bold.ttf:system/fonts/DroidSansHebrew-Bold.ttf \
diff --git a/docs/html/guide/developing/device.jd b/docs/html/guide/developing/device.jd
index e46d07c..d390ec1 100644
--- a/docs/html/guide/developing/device.jd
+++ b/docs/html/guide/developing/device.jd
@@ -254,6 +254,10 @@
     <td><code>04DD</code></td>
   </tr>
   <tr>
+    <td>Sony</td>
+    <td><code>054C</code></td>
+  </tr>
+  <tr>
     <td>Sony Ericsson</td>
     <td><code>0FCE</code></td>
   </tr>
diff --git a/docs/html/guide/practices/tablets-and-handsets.jd b/docs/html/guide/practices/tablets-and-handsets.jd
index 3f4aaa9..8e07a08 100644
--- a/docs/html/guide/practices/tablets-and-handsets.jd
+++ b/docs/html/guide/practices/tablets-and-handsets.jd
@@ -99,7 +99,8 @@
 
 <p>You can enable items from the options menu to appear directly in the action bar as "action
 items". You can also add navigation features to the action bar, such as tabs or a drop-down list,
-and use the application icon to supplement the system's BACK behavior with the option to navigate to
+and use the application icon to supplement the system's <em>Back</em> button behavior with the option to
+navigate to
 your application's "home" activity or "up" the application's structural hierarchy.</p>
 
 <p>This guide provides some tips for using the action bar in ways that support both tablets and
@@ -458,7 +459,8 @@
 developer guide, you can use the application icon in the action bar to facilitate user navigation
 when appropriate&mdash;either as a method to get back to the "home" activity (similar to clicking
 the logo on a web site) or as a way to navigate up the application's structural hierarchy. Although
-it might seem similar to the standard BACK navigation in some cases, the up navigation option
+it might seem similar to the standard <em>Back</em> navigation in some cases, the up navigation
+option
 provides a more predictable navigation method for situations in which the user may have entered
 from an external location, such as a notification, app widget, or a different application.</p>
 
diff --git a/docs/html/guide/practices/ui_guidelines/activity_task_design.jd b/docs/html/guide/practices/ui_guidelines/activity_task_design.jd
index 5faa7ec..9be72ee 100644
--- a/docs/html/guide/practices/ui_guidelines/activity_task_design.jd
+++ b/docs/html/guide/practices/ui_guidelines/activity_task_design.jd
@@ -42,7 +42,8 @@
       <li><a href=#activities_added_to_task_tip>Allow activities to add to current task</a></li>
       <li><a href=#notifications_get_back_tip>Notifications and App Widgets should provide consistent back behavior</li>
       <li><a href=#use_notification_tip>Use the notification system</a></li>
-      <li><a href=#taking_over_back_key>Don't take over BACK key unless you absolutely need to</a></li>
+      <li><a href=#taking_over_back_key>Don't take over <em>Back</em> button unless you absolutely
+need to</a></li>
     </ol>
   </li>
 </ol>
@@ -241,8 +242,8 @@
   Android system keeps a linear navigation history of activities the
   user has visited. This is the <em>activity stack</em>, also known as the
   back stack. In general, when a user starts a new activity, it is added
-  to the activity stack, so that pressing BACK displays the previous
-  activity on the stack. However, the user cannot use the BACK key to go
+  to the activity stack, so that pressing <em>Back</em> displays the previous
+  activity on the stack. However, the user cannot use the <em>Back</em> button to go
   back further than the last visit to Home. The adding of an activity to
   the current stack happens whether or not that activity begins a new 
   <a href=#tasks title=task>task</a> (as long as that task was started
@@ -256,10 +257,11 @@
   Activities are the only things that can be added to the activity stack
   &mdash; views, windows, menus, and dialogs cannot. That is, when
   designing the navigation, if you have screen A and you want the user
-  to be able go to a subsequent screen B and then use the BACK key to go
+  to be able go to a subsequent screen B and then use the <em>Back</em> button to go
   back to screen A, then the screen A needs to be implemented as an
   activity. The one exception to this rule is if your application 
-  <a href="#taking_over_back_key">takes control of the BACK key</a> and manages the navigation
+  <a href="#taking_over_back_key">takes control of the <em>Back</em> button</a> and manages the
+navigation
 itself.
 </p>
 
@@ -287,7 +289,7 @@
   launcher, Home screen shortcut or "Recent tasks" switcher (a long
   press on Home on some devices). The user can return to a task by
   choosing the icon for its root activity the same way they started the
-  task. Once inside a task, the BACK key goes to previous activities in
+  task. Once inside a task, the <em>Back</em> button goes to previous activities in
   that task. The activity stack is made up of one or more tasks.
 </p>
 
@@ -331,7 +333,7 @@
   Browser are two applications that do this. For example, choosing an
   address in an email starts the Maps activity as a new task, and
   choosing a link in an email starts the Browser activity as a new
-  task. In these cases, the BACK key will return to the previous
+  task. In these cases, the <em>Back</em> button will return to the previous
   activity in a different task (Email), because it was not started from
   Home.
 </p>
@@ -341,7 +343,7 @@
 
 <p>
   The following examples illustrate basic principles for applications,
-  activities, the activity stack, the BACK key, tasks and intents.  It
+  activities, the activity stack, the <em>Back</em> button, tasks and intents.  It
   shows how the system responds to user actions such as starting
   activities and switching between tasks.  With most of these examples
   you can follow along, launching activities on your device as
@@ -367,19 +369,20 @@
   <img src={@docRoot}images/activity_task_design/HomeTaskBasics1a.png>
 </p>
 
-<h3 id=navigating_away_from_an_activity>Navigating Away from an Activity with BACK and HOME keys</h3>
+<h3 id=navigating_away_from_an_activity>Navigating Away from an Activity with <em>Back</em> and
+<em>Home</em> buttons</h3>
 
 <p>
   An activity can keep or lose its state depending on how the user
-  leaves the activity &mdash; by the HOME or BACK key.
+  leaves the activity &mdash; by the <em>Home</em> or <em>Back</em> button.
 </p>
 
 <p>
-  By default, pressing the BACK key finishes (destroys) the current
+  By default, pressing the <em>Back</em> button finishes (destroys) the current
   activity and displays the previous activity to the user. In the
   following figure, the user starts email by touching the Email icon in
   the Home screen, which displays a list of email messages. The user
-  scrolls down the list (changing its initial state). Pressing BACK
+  scrolls down the list (changing its initial state). Pressing <em>Back</em>
   destroys the List Messages activity and returns to the previous
   activity, which is Home. If the user re-launches Email, it would
   re-load the messages and display its initial, non-scrolled state.
@@ -390,15 +393,15 @@
 </p>
 
 <p>
-  In the above example, pressing BACK goes to Home because it was the
+  In the above example, pressing <em>Back</em> goes to Home because it was the
   last activity the user was viewing. But if the user had gotten to List
-  Message from some other activity, then pressing BACK would have
+  Message from some other activity, then pressing <em>Back</em> would have
   returned there.
 </p>
 
 <p>
   By contrast, the next figure shows the user leaving List Messages by
-  pressing HOME instead of BACK &mdash; the List Messages activity is
+  pressing <em>Home</em> instead of <em>Back</em> &mdash; the List Messages activity is
   stopped and moved to the background rather than being
   destroyed. Starting Email again from its icon would simply bring the
   List Messages activity to the foreground (changing it from stopped to
@@ -423,8 +426,8 @@
 
 <p>
   In addition, not all activities have the behavior that they are
-  destroyed when BACK is pressed. When the user starts playing music in
-  the Music application and then presses BACK, the application overrides
+  destroyed when <em>Back</em> is pressed. When the user starts playing music in
+  the Music application and then presses <em>Back</em>, the application overrides
   the normal back behavior, preventing the player activity from being
   destroyed, and continues playing music, even though its activity is no
   longer visible &mdash; as a visual substitute, the Music application
@@ -451,7 +454,7 @@
   activity to get a picture. This is a good example of re-use of the
   Gallery activity. The following figure illustrates the sequence of
   activities to do this (up to crop). This is how it's done: The user
-  chooses Contacts, selects the contact for viewing, chooses MENU &gt;
+  chooses Contacts, selects the contact for viewing, chooses <em>Menu</em> &gt;
   Edit contact and touches the picture field, which launches the Gallery
   activity. The user then chooses the picture they want, crops and saves
   it. Saving it causes the picture to be inserted into the picture field
@@ -484,12 +487,12 @@
   <b>Gallery Re-Uses Messaging for Sharing a Picture</b> - Sharing is
   another good example of one application re-using an activity from a
   different application. As shown in the following figure, the user
-  starts Gallery, picks a picture to view, chooses MENU &gt; Share, and
+  starts Gallery, picks a picture to view, chooses <em>Menu</em> &gt; Share, and
   picks "Messaging". This starts the Messaging activity, creates a new
   message and attaches the original picture to it. The user then fills
   in the "To" field, writes a short message and sends it. User focus
   remains in the Messaging program. If the user wants to go back to the
-  Gallery, they must press the BACK key. (The user can back up through
+  Gallery, they must press the <em>Back</em> button. (The user can back up through
   each activity all the way to Home.)
 </p>
 
@@ -552,7 +555,7 @@
   <ul>
     <li>
       State 2 - The user wants to do something else while they're
-      waiting, so they press HOME, which does not interrupt the map's
+      waiting, so they press <em>Home</em>, which does not interrupt the map's
       network connection and allows the map to continue loading in the
       background.
 
@@ -729,7 +732,7 @@
         <b>Start first task.</b> You want to send a text message and attach a photo. You would choose:
 
         <p>
-          Home &gt; Messaging &gt; New message &gt; MENU &gt; Attach
+          Home &gt; Messaging &gt; New message &gt; <em>Menu</em> &gt; Attach
           &gt; Pictures. This last step launches the picture gallery
           for picking a photo. Notice that picture gallery is an
           activity in a separate application.
@@ -961,7 +964,7 @@
           address in an email message (or web page), where the Maps
           activity is started to map the location. No result from maps
           is expected to be returned to the email message; the user
-          can return by pressing the BACK key. (Such an activity is
+          can return by pressing the <em>Back</em> button. (Such an activity is
           started with {@link
           android.content.Context#startActivity(android.content.Intent)
           startActivity()}.)
@@ -1102,20 +1105,21 @@
   convenience to respond to your message.
 </p>
 
-<h3 id=taking_over_back_key>Don't take over the BACK key unless you absolutely need to</h3>
+<h3 id=taking_over_back_key>Don't take over the <em>Back</em> button unless you absolutely need
+to</h3>
 
 <p>
   As a user navigates from one activity to the next, the system adds
   them to the activity stack. This forms a navigation history that is
-  accessible with the BACK key. Most activities are relatively limited
+  accessible with the <em>Back</em> button. Most activities are relatively limited
   in scope, with just one set of data, such as viewing a list of
   contacts, composing an email, or taking a photo. But what if your
   application is one big activity with several pages of content and
-  needs finer-grained control of the BACK key? Examples of such Google
+  needs finer-grained control of the <em>Back</em> button? Examples of such Google
   applications are the Browser, which can have several web pages open
   at once, and Maps, which can have several layers of geographic data
   to switch between. Both of these applications take control of the
-  BACK key and maintain their own internal back stacks that operate
+  <em>Back</em> button and maintain their own internal back stacks that operate
   only when these applications have focus.
 </p>
 
@@ -1124,7 +1128,7 @@
   information on a map to the user: displaying the location of a
   search result, displaying locations of friends, and displaying a
   line for a street path providing direction between points. Maps
-  stores these layers in its own history so the BACK key can return to
+  stores these layers in its own history so the <em>Back</em> button can return to
   a previous layer.
 </p>
 
@@ -1135,8 +1139,8 @@
   as Windows, Macintosh or Linux). For example, if you did a Google
   web search in one window of the Android Browser, clicking on a link
   in the search results displays a web page in that same window, and
-  then pressing BACK would to the search results page. Pressing
-  BACK goes to a previous window only if the current window was
+  then pressing <em>Back</em> would to the search results page. Pressing
+  <em>Back</em> goes to a previous window only if the current window was
   launched from that previous window.  If the user keeps pressing
   back, they will eventually leave the browser activity and return
   Home.
diff --git a/docs/html/guide/practices/ui_guidelines/menu_design.jd b/docs/html/guide/practices/ui_guidelines/menu_design.jd
index 3edf33f..7576b6c 100644
--- a/docs/html/guide/practices/ui_guidelines/menu_design.jd
+++ b/docs/html/guide/practices/ui_guidelines/menu_design.jd
@@ -71,7 +71,7 @@
   <ul>
     <li>The <em>Options menu</em> contains primary functionality that applies
         globally to the current activity or starts a related activity. 
-        It is typically invoked by a user pressing a hard button, often labeled MENU.</li>
+        It is typically invoked by a user pressing a hard button, often labeled <em>Menu</em>.</li>
     <li>The <em>Context menu</em> contains secondary functionality for the currently
         selected item.  It is typically invoked by a user's touch &amp; hold
         on an item.  Like on the Options menu, the operation can run either
@@ -109,10 +109,10 @@
 </p>
 
 <p>
-  On most devices, a user presses the MENU button to access the Options menu, 
+  On most devices, a user presses the <em>Menu</em> button to access the Options menu, 
   as shown in the screenshot below.  To close the menu, the user presses 
-  MENU again, or presses the BACK button. 
-  In fact, to cancel out of any menu, press the BACK button.  (Pressing the MENU
+  <em>Menu</em> again, or presses the <em>Back</em> button. 
+  In fact, to cancel out of any menu, press the <em>Back</em> button.  (Pressing the <em>Menu</em>
   button or touching outside the menu also works.)  Note that how to invoke this
   menu may be different on different devices.
 </p>
@@ -140,7 +140,7 @@
 
 <ul>
   <li>
-    <b>Options icon menu</b> - The first press of the MENU button displays a
+    <b>Options icon menu</b> - The first press of the <em>Menu</em> button displays a
     non-scrollable grid of icons at the bottom of the screen.  (On the G1
     phone, up to 6 buttons typically appear.)
   </li>
@@ -156,7 +156,7 @@
 
 <p>
   On some versions of Android, the user can display keyboard shortcuts in the
-  icon menu by long pressing the MENU button &mdash; the text in the icon menu
+  icon menu by long pressing the <em>Menu</em> button &mdash; the text in the icon menu
   alternates between the command names and their keyboard shortcuts (if any).
 </p>
 
@@ -299,7 +299,7 @@
   <a href="#location">location</a>) on the screen, put the command in the
   Context menu for that content.  If the command acts on no specific content
   or location, put it in the Options menu.  This separation of commands
-  is enforced by the system in the following way.  When you press the MENU
+  is enforced by the system in the following way.  When you press the <em>Menu</em>
   button to display the Options menu, the selected content becomes unselected,
   and so cannot be operated on.  For an explanation
   of why the content becomes unselected, see the article on
@@ -340,7 +340,7 @@
 
 <p>
   Before opening a Context menu, it has no visual representation that identifies
-  its presence (whereas the Options menu has the MENU button), and so is not
+  its presence (whereas the Options menu has the <em>Menu</em> button), and so is not
   particularly discoverable. 
   Therefore, in general, a Context menu should <em>duplicate</em> commands 
   found in the corresponding activity screen.  For example, while it's useful to
@@ -459,7 +459,8 @@
 <h3 id="a_dialog_should_not_have_an_options_menu">A dialog should not have an Options menu</h3>
 
 <p>
-  When a dialog is displayed, pressing the MENU button should do nothing.  This also holds true
+  When a dialog is displayed, pressing the <em>Menu</em> button should do nothing.  This also holds
+true
   for activities that look like dialogs.  A dialog box is recognizable by being
   smaller than full-screen, having zero to three buttons, is non-scrollable, and 
   possibly a list of selectable items that can include checkboxes or radio buttons.
@@ -475,7 +476,7 @@
 <h3 id="do_not_substitute_message">If an activity has no Options menu, do not display a message</h3>
 
 <p>
-  When the user presses the MENU button, if there is no Options menu, the system
+  When the user presses the <em>Menu</em> button, if there is no Options menu, the system
   currently does nothing.  We recommend you do not perform any action (such as
   displaying a message).  It's a better user experience for this behavior to be
   consistent across applications.
diff --git a/docs/html/guide/topics/fundamentals/activities.jd b/docs/html/guide/topics/fundamentals/activities.jd
index 3b31199..8736aa8 100644
--- a/docs/html/guide/topics/fundamentals/activities.jd
+++ b/docs/html/guide/topics/fundamentals/activities.jd
@@ -63,7 +63,7 @@
 activity starts, the previous activity is stopped, but the system preserves the activity
 in a stack (the "back stack"). When a new activity starts, it is pushed onto the back stack and
 takes user focus. The back stack abides to the basic "last in, first out" queue mechanism,
-so, when the user is done with the current activity and presses the BACK key, it
+so, when the user is done with the current activity and presses the <em>Back</em> button, it
 is popped from the stack (and destroyed) and the previous activity resumes. (The back stack is
 discussed more in the <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks
 and Back Stack</a> document.)</p>
@@ -649,7 +649,8 @@
 <p class="note"><strong>Note:</strong> There's no guarantee that {@link
 android.app.Activity#onSaveInstanceState onSaveInstanceState()} will be called before your
 activity is destroyed, because there are cases in which it won't be necessary to save the state
-(such as when the user leaves your activity using the BACK key, because the user is explicitly
+(such as when the user leaves your activity using the <em>Back</em> button, because the user is
+explicitly
 closing the activity). If the system calls {@link android.app.Activity#onSaveInstanceState
 onSaveInstanceState()}, it does so before {@link
 android.app.Activity#onStop onStop()} and possibly before {@link android.app.Activity#onPause
diff --git a/docs/html/guide/topics/fundamentals/fragments.jd b/docs/html/guide/topics/fundamentals/fragments.jd
index e8f6cd8..d4f9342 100644
--- a/docs/html/guide/topics/fundamentals/fragments.jd
+++ b/docs/html/guide/topics/fundamentals/fragments.jd
@@ -78,7 +78,7 @@
 fragment transaction, you can also add it to a back stack that's managed by the
 activity&mdash;each back stack entry in the activity is a record of the fragment transaction that
 occurred. The back stack allows the user to reverse a fragment transaction (navigate backwards),
-by pressing the BACK button.</p>
+by pressing the <em>Back</em> button.</p>
 
 <p>When you add a fragment as a part of your activity layout, it lives in a {@link
 android.view.ViewGroup} inside the activity's view hierarchy and the fragment defines its own view
@@ -398,7 +398,7 @@
 the activity layout) or {@link android.app.FragmentManager#findFragmentByTag
 findFragmentByTag()} (for fragments that do or don't provide a UI).</li> 
   <li>Pop fragments off the back stack, with {@link
-android.app.FragmentManager#popBackStack()} (simulating a BACK command by the user).</li>
+android.app.FragmentManager#popBackStack()} (simulating a <em>Back</em> command by the user).</li>
   <li>Register a listener for changes to the back stack, with {@link
 android.app.FragmentManager#addOnBackStackChangedListener addOnBackStackChangedListener()}.</li>
 </ul>
@@ -439,7 +439,7 @@
 android.app.FragmentTransaction#commit()}, however, you might want to call {@link
 android.app.FragmentTransaction#addToBackStack addToBackStack()}, in order to add the transaction
 to a back stack of fragment transactions. This back stack is managed by the activity and allows
-the user to return to the previous fragment state, by pressing the BACK key.</p>
+the user to return to the previous fragment state, by pressing the <em>Back</em> button.</p>
 
 <p>For example, here's how you can replace one fragment with another, and preserve the previous
 state in the back stack:</p>
@@ -462,14 +462,14 @@
 layout container identified by the {@code R.id.fragment_container} ID. By calling {@link
 android.app.FragmentTransaction#addToBackStack addToBackStack()}, the replace transaction is
 saved to the back stack so the user can reverse the transaction and bring back the
-previous fragment by pressing the BACK key.</p>
+previous fragment by pressing the <em>Back</em> button.</p>
 
 <p>If you add multiple changes to the transaction (such as another {@link
 android.app.FragmentTransaction#add add()} or {@link android.app.FragmentTransaction#remove
 remove()}) and call {@link
 android.app.FragmentTransaction#addToBackStack addToBackStack()}, then all changes applied
 before you call {@link android.app.FragmentTransaction#commit commit()} are added to the
-back stack as a single transaction and the BACK key will reverse them all together.</p>
+back stack as a single transaction and the <em>Back</em> button will reverse them all together.</p>
 
 <p>The order in which you add changes to a {@link android.app.FragmentTransaction} doesn't matter,
 except:</p>
@@ -696,7 +696,7 @@
 <p>The most significant difference in lifecycle between an activity and a fragment is how one is
 stored in its respective back stack. An activity is placed into a back stack of activities
 that's managed by the system when it's stopped, by default (so that the user can navigate back
-to it with the BACK key, as discussed in <a
+to it with the <em>Back</em> button, as discussed in <a
 href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back Stack</a>).
 However, a fragment is placed into a back stack managed by the host activity only when you
 explicitly request that the instance be saved by calling {@link
diff --git a/docs/html/guide/topics/fundamentals/tasks-and-back-stack.jd b/docs/html/guide/topics/fundamentals/tasks-and-back-stack.jd
index 086ba71..465cf54 100644
--- a/docs/html/guide/topics/fundamentals/tasks-and-back-stack.jd
+++ b/docs/html/guide/topics/fundamentals/tasks-and-back-stack.jd
@@ -74,7 +74,7 @@
 suppose you have a two-pane layout using fragments, one of which is a list view (fragment A) and the
 other being a layout to display an item from the list (fragment B). When the user selects an item
 from the list, fragment B is replaced by a new fragment (fragment C). In this case, it might be
-desireable for the user to navigate back to reveal fragment B, using the BACK button.</p>
+desireable for the user to navigate back to reveal fragment B, using the <em>Back</em> button.</p>
 <p>In order to add fragment B to the back stack so that this is possible, you must call {@link
 android.app.FragmentTransaction#addToBackStack addToBackStack()} before you {@link
 android.app.FragmentTransaction#commit()} the transaction that replaces fragment B with fragment
@@ -94,22 +94,26 @@
 
 <p>When the current activity starts another, the new activity is pushed on the top of the stack and
 takes focus. The previous activity remains in the stack, but is stopped. When an activity
-stops, the system retains the current state of its user interface. When the user presses the BACK
+stops, the system retains the current state of its user interface. When the user presses the
+<em>Back</em>
 button, the current activity is popped from the top of the stack (the activity is destroyed) and the
 previous activity resumes (the previous state of its UI is restored). Activities in the stack are
 never rearranged, only pushed and popped from the stack&mdash;pushed onto the stack when started by
-the current activity and popped off when the user leaves it using the BACK button. As such, the back
+the current activity and popped off when the user leaves it using the <em>Back</em> button. As such,
+the back
 stack operates as a "last in, first out" object structure. Figure 1 visualizes
 this behavior with a timeline showing the progress between activities along with the current back
 stack at each point in time.</p>
 
 <img src="{@docRoot}images/fundamentals/diagram_backstack.png" alt="" />
 <p class="img-caption"><strong>Figure 1.</strong> A representation of how each new activity in a
-task adds an item to the back stack. When the user presses the BACK button, the current activity is
+task adds an item to the back stack. When the user presses the <em>Back</em> button, the current
+activity is
 destroyed and the previous activity resumes.</p>
 
 
-<p>If the user continues to press BACK, then each activity in the stack is popped off to reveal the
+<p>If the user continues to press <em>Back</em>, then each activity in the stack is popped off to
+reveal the
 previous one, until the user returns to the Home screen (or to whichever activity was running when
 the task began). When all activities are removed from the stack, the task no longer exists.</p>
 
@@ -124,11 +128,13 @@
 </div>
 
 <p>A task is a cohesive unit that can move to the "background" when users begin a new task or go
-to the Home screen, via the HOME button. While in the background, all the activities in the task are
+to the Home screen, via the <em>Home</em> button. While in the background, all the activities in the
+task are
 stopped, but the back stack for the task remains intact&mdash;the task has simply lost focus while
 another task takes place, as shown in figure 2. A task can then return to the "foreground" so users
 can pick up where they left off. Suppose, for example, that the current task (Task A) has three
-activities in its stack&mdash;two under the current activity. The user presses the HOME button, then
+activities in its stack&mdash;two under the current activity. The user presses the <em>Home</em>
+button, then
 starts a new application from the application launcher. When the Home screen appears, Task A goes
 into the background. When the new application starts, the system starts a task for that application
 (Task B) with its own stack of activities. After interacting with
@@ -137,7 +143,8 @@
 foreground&mdash;all three activities in its stack are intact and the activity at the top of the
 stack resumes. At
 this point, the user can also switch back to Task B by going Home and selecting the application icon
-that started that task (or by touching and holding the HOME button to reveal recent tasks and selecting
+that started that task (or by touching and holding the <em>Home</em> button to reveal recent tasks
+and selecting
 one). This is an example of multitasking on Android.</p>
 
 <p class="note"><strong>Note:</strong> Multiple tasks can be held in the background at once.
@@ -150,7 +157,8 @@
 that activity is created and popped onto the stack (rather than bringing any previous instance of
 the activity to the top). As such, one activity in your application might be instantiated multiple
 times (even from different tasks), as shown in figure 3. As such, if the user navigates backward
-using the BACK button, each instance of the activity is revealed in the order they were opened (each
+using the <em>Back</em> button, each instance of the activity is revealed in the order they were
+opened (each
 with their own UI state). However, you can modify this behavior if you do not want an activity to be
 instantiated more than once. How to do so is discussed in the later section about <a
 href="#ManagingTasks">Managing Tasks</a>.</p>
@@ -161,13 +169,15 @@
 <ul>
   <li>When Activity A starts Activity B, Activity A is stopped, but the system retains its state
 (such as scroll position and text entered into forms).
-If the user presses the BACK button while in Activity B, Activity A resumes with its state
+If the user presses the <em>Back</em> button while in Activity B, Activity A resumes with its state
 restored.</li>
-  <li>When the user leaves a task by pressing the HOME button, the current activity is stopped and
+  <li>When the user leaves a task by pressing the <em>Home</em> button, the current activity is
+stopped and
 its task goes into the background. The system retains the state of every activity in the task. If
 the user later resumes the task by selecting the launcher icon that began the task, the task comes
 to the foreground and resumes the activity at the top of the stack.</li>
-  <li>If the user presses the BACK button, the current activity is popped from the stack and
+  <li>If the user presses the <em>Back</em> button, the current activity is popped from the stack
+and
 destroyed. The previous activity in the stack is resumed. When an activity is destroyed, the system
 <em>does not</em> retain the activity's state.</li>
   <li>Activities can be instantiated multiple times, even from other tasks.</li>
@@ -256,7 +266,8 @@
 <p class="caution"><strong>Caution:</strong> Most applications should not interrupt the default
 behavior for activities and tasks. If you determine that it's necessary for your activity to modify
 the default behaviors, use caution and be sure to test the usability of the activity during
-launch and when navigating back to it from other activities and tasks with the BACK button. Be sure 
+launch and when navigating back to it from other activities and tasks with the <em>Back</em> button.
+Be sure 
 to test for navigation behaviors that might conflict with the user's expected behavior.</p>
 
 
@@ -320,8 +331,10 @@
 stack remains A-B-C-D. However, if an intent arrives for an activity of type B, then a new
 instance of B is added to the stack, even if its launch mode is {@code "singleTop"}.</p>
   <p class="note"><strong>Note:</strong> When a new instance of an activity is created,
-the user can press the BACK button to return to the previous activity. But when an existing instance of
-an activity handles a new intent, the user cannot press the BACK button to return to the state of
+the user can press the <em>Back</em> button to return to the previous activity. But when an existing
+instance of
+an activity handles a new intent, the user cannot press the <em>Back</em> button to return to the
+state of
 the activity before the new intent arrived in {@link android.app.Activity#onNewIntent
 onNewIntent()}.</p>
 </dd>
@@ -333,7 +346,7 @@
 android.app.Activity#onNewIntent onNewIntent()} method, rather than creating a new instance. Only
 one instance of the activity can exist at a time.
   <p class="note"><strong>Note:</strong> Although the activity starts in a new task, the
-BACK button still returns the user to the previous activity.</p></dd>
+<em>Back</em> button still returns the user to the previous activity.</p></dd>
 <dt>{@code "singleInstance"}.</dt>
   <dd>Same as {@code "singleTask"}, except that the system doesn't launch any other activities into
 the task holding the instance. The activity is always the single and only member of its task;
@@ -351,7 +364,7 @@
 intent.</p>
 
 <p>Regardless of whether an activity starts in a new task or in the same task as the activity that
-started it, the BACK button always takes the user to the previous activity. However, if you
+started it, the <em>Back</em> button always takes the user to the previous activity. However, if you
 start an activity that specifies the {@code singleTask} launch mode, then if an instance of
 that activity exists in a background task, that whole task is brought to the foreground. At this
 point, the back stack now includes all activities from the task brought forward, at the top of the
@@ -454,7 +467,8 @@
 However, it doesn't have to be.  If there's already an existing task with the same affinity as the
 new activity, the activity is launched into that task.  If not, it begins a new task.</p>
 
-<p>If this flag causes an activity to begin a new task and the user presses the HOME button to leave
+<p>If this flag causes an activity to begin a new task and the user presses the <em>Home</em> button
+to leave
 it, there must be some way for the user to navigate back to the task. Some entities (such as the
 notification manager) always start activities in an external task, never as part of their own, so
 they always put {@code FLAG_ACTIVITY_NEW_TASK} in the intents they pass to {@link
@@ -556,7 +570,8 @@
 and a {@link android.content.Intent#CATEGORY_LAUNCHER}
 filter. Imagine, for example, what could happen if the filter is missing: An intent launches a
 {@code "singleTask"} activity, initiating a new task, and the user spends some time working in
-that task.  The user then presses the HOME button. The task is now sent to the background and is
+that task.  The user then presses the <em>Home</em> button. The task is now sent to the background
+and is
 not visible. Now the user has no way to return to the task, because it is not represented in the
 application launcher.
 </p>
diff --git a/docs/html/guide/topics/intents/intents-filters.jd b/docs/html/guide/topics/intents/intents-filters.jd
index 3f94553..3ad3c93 100644
--- a/docs/html/guide/topics/intents/intents-filters.jd
+++ b/docs/html/guide/topics/intents/intents-filters.jd
@@ -247,7 +247,7 @@
 </tr><tr>
    <td>{@code CATEGORY_HOME}
    <td>The activity displays the home screen, the first screen the user sees when 
-       the device is turned on or when the HOME key is pressed.
+       the device is turned on or when the <em>Home</em> button is pressed.
 </tr><tr>
    <td>{@code CATEGORY_LAUNCHER}
    <td>The activity can be the initial activity of a task and is listed in 
diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd
index e76a6be..4d9603f 100644
--- a/docs/html/guide/topics/manifest/activity-element.jd
+++ b/docs/html/guide/topics/manifest/activity-element.jd
@@ -133,21 +133,21 @@
 it's ignored for all other activities in the task.
 
 <p>
-When the value is "{@code true}", every time users start the task again, they 
-are brought to its root activity, regardless of what they were last doing in 
-the task and regardless of whether they used BACK or HOME to last leave it.  
-When the value is "{@code false}", the task may be cleared of activities in 
+When the value is "{@code true}", every time users start the task again, they
+are brought to its root activity regardless of what they were last doing in
+the task and regardless of whether they used the <em>Back</em> or <em>Home</em> button to
+leave it. When the value is "{@code false}", the task may be cleared of activities in
 some situations (see the 
 <code><a href="#always">alwaysRetainTaskState</a></code> attribute), but not always.  
 </p>
 
 <p>
 Suppose, for example, that someone launches activity P from the home screen, 
-and from there goes to activity Q.  The user next presses HOME, and then returns 
+and from there goes to activity Q.  The user next presses <em>Home</em>, and then returns 
 to activity P.  Normally, the user would see activity Q, since that is what they 
 were last doing in P's task.  However, if P set this flag to "{@code true}", all 
 of the activities on top of it (Q in this case) were removed when the user pressed 
-HOME and the task went to the background.  So the user sees only P when returning 
+<em>Home</em> and the task went to the background.  So the user sees only P when returning 
 to the task.
 </p>
 
@@ -501,7 +501,7 @@
 
 <p>Regardless of the launch mode that you choose, make sure to test the usability
 of the activity during launch and when navigating back to it from
-other activities and tasks using the BACK key. </p>
+other activities and tasks using the <em>Back</em> button. </p>
 
 <p>For more information on launch modes and their interaction with Intent
 flags, see the 
diff --git a/docs/html/guide/topics/search/search-dialog.jd b/docs/html/guide/topics/search/search-dialog.jd
index e06563d..8b8e75b 100644
--- a/docs/html/guide/topics/search/search-dialog.jd
+++ b/docs/html/guide/topics/search/search-dialog.jd
@@ -544,7 +544,8 @@
 }
 </pre>
 
-<p>If the user cancels search by pressing the BACK button, the search dialog closes and the activity
+<p>If the user cancels search by pressing the <em>Back</em> button, the search dialog closes and the
+activity
 regains input focus. You can register to be notified when the search dialog is
 closed with {@link android.app.SearchManager#setOnDismissListener(SearchManager.OnDismissListener)
 setOnDismissListener()}
@@ -569,7 +570,8 @@
 android.content.Intent#ACTION_SEARCH} intent with a call to {@link
 android.app.Activity#onCreate(Bundle) onCreate()} and a new instance of the
 activity is brought to the top of the activity stack. There are now two instances of your
-searchable activity in the activity stack (so pressing the BACK button goes back to the previous
+searchable activity in the activity stack (so pressing the <em>Back</em> button goes back to the
+previous
 instance of the searchable activity, rather than exiting the searchable activity).</li>
   <li>If you set {@code android:launchMode} to <code>"singleTop"</code>, then the
 searchable activity receives the {@link android.content.Intent#ACTION_SEARCH} intent with a call
diff --git a/docs/html/guide/topics/ui/index.jd b/docs/html/guide/topics/ui/index.jd
index d3060c5..83c8150 100644
--- a/docs/html/guide/topics/ui/index.jd
+++ b/docs/html/guide/topics/ui/index.jd
@@ -174,7 +174,8 @@
 
 <p>Application menus are another important part of an application's UI. Menus offers a reliable interface that reveals
 application functions and settings. The most common application menu is revealed by pressing
-the MENU key on the device. However, you can also add Context Menus, which may be revealed when the user presses
+the <em>Menu</em> button on the device. However, you can also add Context Menus, which may be
+revealed when the user presses
 and holds down on an item.</p>
 
 <p>Menus are also structured using a View hierarchy, but you don't define this structure yourself. Instead,
diff --git a/docs/html/guide/topics/ui/notifiers/notifications.jd b/docs/html/guide/topics/ui/notifiers/notifications.jd
index bef9671..d104b4b 100644
--- a/docs/html/guide/topics/ui/notifiers/notifications.jd
+++ b/docs/html/guide/topics/ui/notifiers/notifications.jd
@@ -173,7 +173,7 @@
   </li>
   <li>
     The user has seen enough to know they have a meeting coming up,
-    so they press the BACK button.  They are now returned to Email, which
+    so they press the <em>Back</em> button.  They are now returned to Email, which
     is where they were when they took the notification.
   </li>
 </ol>
@@ -198,8 +198,8 @@
     (writing an e-mail), but that message is still saved in their drafts.
   </li>
   <li>
-    The user presses BACK once to go to the message list (the typical flow in the
-    Email app), and press BACK again to return to Calendar as they left it.
+    The user presses <em>Back</em> once to go to the message list (the typical flow in the
+    Email app), and press <em>Back</em> again to return to Calendar as they left it.
   </li>
 </ol>
 
@@ -216,7 +216,7 @@
 most interest is the <code>makeMessageIntentStack()</code> method, which constructs
 an array of intents representing the app's new activity stack for this state.
 (If you are using fragments, you may need to initialize your fragment and
-app state so that pressing BACK will switch the UI back to its parent state.)
+app state so that pressing <em>Back</em> will switch the UI back to its parent state.)
 The core of this is the {@link android.content.Intent#makeRestartActivityTask
 Intent.makeRestartActivityTask()} method, which constructs the root activity
 of the stack with the appropriate flags, such as
diff --git a/docs/html/guide/webapps/webview.jd b/docs/html/guide/webapps/webview.jd
index ed28f21..66b5501 100644
--- a/docs/html/guide/webapps/webview.jd
+++ b/docs/html/guide/webapps/webview.jd
@@ -298,18 +298,18 @@
 pages. You can navigate backward and forward through the history with {@link
 android.webkit.WebView#goBack()} and {@link android.webkit.WebView#goForward()}.</p>
 
-<p>For example, here's how your {@link android.app.Activity} can use the device BACK key to navigate
-backward:</p>
+<p>For example, here's how your {@link android.app.Activity} can use the device <em>Back</em> button
+to navigate backward:</p>
 
 <pre>
 &#64;Override
 public boolean {@link android.app.Activity#onKeyDown(int,KeyEvent) onKeyDown}(int keyCode, KeyEvent event) {
-    // Check if the key event was the BACK key and if there's history
+    // Check if the key event was the Back button and if there's history
     if ((keyCode == KeyEvent.KEYCODE_BACK) &amp;&amp; myWebView.{@link android.webkit.WebView#canGoBack() canGoBack}() {
         myWebView.{@link android.webkit.WebView#goBack() goBack}();
         return true;
     }
-    // If it wasn't the BACK key or there's no web page history, bubble up to the default
+    // If it wasn't the Back key or there's no web page history, bubble up to the default
     // system behavior (probably exit the activity)
     return super.onKeyDown(keyCode, event);
 }
diff --git a/docs/html/training/design-navigation/ancestral-temporal.jd b/docs/html/training/design-navigation/ancestral-temporal.jd
index c0b1aef..ab6a64d 100644
--- a/docs/html/training/design-navigation/ancestral-temporal.jd
+++ b/docs/html/training/design-navigation/ancestral-temporal.jd
@@ -29,7 +29,10 @@
 </div>
 
 
-<p>Now that users can navigate <a href="descendant-lateral.html">deep into</a> the application's screen hierarchy, we need to provide a method for navigating up the hierarchy, to parent and ancestor screens. Additionally, we should ensure that temporal navigation via the BACK button is respected to respect Android conventions.</p>
+<p>Now that users can navigate <a href="descendant-lateral.html">deep into</a> the application's
+screen hierarchy, we need to provide a method for navigating up the hierarchy, to parent and
+ancestor screens. Additionally, we should ensure that temporal navigation via the <em>Back</em>
+button is respected to respect Android conventions.</p>
 
 <div class="design-announce">
 <p><strong>Back/Up Navigation Design</strong></p>
@@ -39,27 +42,52 @@
 
 <h2 id="temporal-navigation">Support Temporal Navigation: <em>Back</em></h2>
 
-<p>Temporal navigation, or navigation between historical screens, is deeply rooted in the Android system. All Android users expect the BACK button to take them to the previous screen, regardless of other state. The set of historical screens is always rooted at the user's Launcher application (the phone's "home" screen). That is, pressing BACK enough times should land you back at the Launcher, after which the BACK button will do nothing.</p>
+<p>Temporal navigation, or navigation between historical screens, is deeply rooted in the Android
+system. All Android users expect the <em>Back</em> button to take them to the previous screen,
+regardless of other state. The set of historical screens is always rooted at the user's Launcher
+application (the phone's "home" screen). That is, pressing <em>Back</em> enough times should land
+you back at the Launcher, after which the <em>Back</em> button will do nothing.</p>
 
 
 <img src="{@docRoot}images/training/app-navigation-ancestral-navigate-back.png"
-  alt="The BACK button behavior after entering the Email app from the People (or Contacts) app" id="figure-navigate-back">
+  alt="The Back button behavior after entering the Email app from the People (or Contacts) app"
+id="figure-navigate-back">
 
-<p class="img-caption"><strong>Figure 1.</strong> The BACK button behavior after entering the Email app from the People (or Contacts) app.</p>
+<p class="img-caption"><strong>Figure 1.</strong> The <em>Back</em> button behavior after entering
+the Email app from the People (or Contacts) app.</p>
 
 
-<p>Applications generally don't have to worry about managing the BACK button themselves; the system handles <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">tasks and the <em>back stack</em></a>, or the list of previous screens, automatically. The BACK button by default simply traverses this list of screens, removing the current screen from the list upon being pressed.</p>
+<p>Applications generally don't have to worry about managing the <em>Back</em> button themselves;
+the system handles <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">tasks and
+the <em>back stack</em></a>, or the list of previous screens, automatically. The <em>Back</em>
+button by default simply traverses this list of screens, removing the current screen from the list
+upon being pressed.</p>
 
-<p>There are, however, cases where you may want to override the behavior for BACK. For example, if your screen contains an embedded web browser where users can interact with page elements to navigate between web pages, you may wish to trigger the embedded browser's default <em>back</em> behavior when users press the device's BACK button. Upon reaching the beginning of the browser's internal history, you should always defer to the system's default behavior for the BACK button.</p>
+<p>There are, however, cases where you may want to override the behavior for <em>Back</em>. For
+example, if your screen contains an embedded web browser where users can interact with page elements
+to navigate between web pages, you may wish to trigger the embedded browser's default <em>back</em>
+behavior when users press the device's <em>Back</em> button. Upon reaching the beginning of the
+browser's internal history, you should always defer to the system's default behavior for the
+<em>Back</em> button.</p>
 
 
 <h2 id="ancestral-navigation">Provide Ancestral Navigation: <em>Up</em> and <em>Home</em></h2>
 
-<p>Before Android 3.0, the most common form of ancestral navigation was the <em>Home</em> metaphor. This was generally implemented as a <em>Home</em> item accessible via the device's MENU button, or a <em>Home</em> button at the top-left of the screen, usually as a component of the Action Bar (<a href="{@docRoot}design/patterns/actionbar.html">pattern docs</a> at Android Design). Upon selecting <em>Home</em>, the user would be taken to the screen at the top of the screen hierarchy, generally known as the application's home screen.</p>
+<p>Before Android 3.0, the most common form of ancestral navigation was the <em>Home</em> metaphor.
+This was generally implemented as a <em>Home</em> item accessible via the device's <em>Menu</em>
+button, or a <em>Home</em> button at the top-left of the screen, usually as a component of the
+Action Bar (<a href="{@docRoot}design/patterns/actionbar.html">pattern docs</a> at Android Design).
+Upon selecting <em>Home</em>, the user would be taken to the screen at the top of the screen
+hierarchy, generally known as the application's home screen.</p>
 
 <p>Providing direct access to the application's home screen can give the user a sense of comfort and security. Regardless of where they are in the application, if they get lost in the app, they can select <em>Home</em> to arrive back at the familiar home screen.</p>
 
-<p>Android 3.0 introduced the <em>Up</em> metaphor, which is presented in the Action Bar as a substitute for the <em>Home</em> button described above. Upon tapping <em>Up</em>, the user should be taken to the parent screen in the hierarchy. This navigation step is usually the previous screen (as described with the BACK button discussion above), but this is not universally the case. Thus, developers must ensure that <em>Up</em> for each screen navigates to a single, predetermined parent screen.</p>
+<p>Android 3.0 introduced the <em>Up</em> metaphor, which is presented in the Action Bar as a
+substitute for the <em>Home</em> button described above. Upon tapping <em>Up</em>, the user should
+be taken to the parent screen in the hierarchy. This navigation step is usually the previous screen
+(as described with the <em>Back</em> button discussion above), but this is not universally the case.
+Thus, developers must ensure that <em>Up</em> for each screen navigates to a single, predetermined
+parent screen.</p>
 
 
 <img src="{@docRoot}images/training/app-navigation-ancestral-navigate-up.png"
@@ -70,6 +98,12 @@
 
 <p>In some cases, it's appropriate for <em>Up</em> to perform an action rather than navigating to a parent screen. Take for example, the Gmail application for Android 3.0-based tablets. When viewing a mail conversation while holding the device in landscape, the conversation list, as well as the conversation details are presented side-by-side. This is a form of parent-child screen grouping, as discussed in a <a href="multiple-sizes.html">previous lesson</a>. However, when viewing a mail conversation in the portrait orientation, only the conversation details are shown. The <em>Up</em> button is used to temporarily show the parent pane, which slides in from the left of the screen. Pressing the <em>Up</em> button again while the left pane is visible exits the context of the individual conversation, up to a full-screen list of conversations.</p>
 
-<p class="note"><strong>Implementation Note:</strong> As a best practice, when implementing either <em>Home</em> or <em>Up</em>, make sure to clear the back stack of any descendent screens. For <em>Home</em>, the only remaining screen on the back stack should be the home screen. For <em>Up</em> navigation, the current screen should be removed from the back stack, unless BACK navigates across screen hierarchies. You can use the {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP} and {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} intent flags together to achieve this.</p>
+<p class="note"><strong>Implementation Note:</strong> As a best practice, when implementing either
+<em>Home</em> or <em>Up</em>, make sure to clear the back stack of any descendent screens. For
+<em>Home</em>, the only remaining screen on the back stack should be the home screen. For
+<em>Up</em> navigation, the current screen should be removed from the back stack, unless
+<em>Back</em> navigates across screen hierarchies. You can use the {@link
+android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP} and {@link
+android.content.Intent#FLAG_ACTIVITY_NEW_TASK} intent flags together to achieve this.</p>
 
 <p>In the last lesson, we apply the concepts discussed in all of the lessons so far to create interaction design wireframes for our example news application.</p>
diff --git a/docs/html/training/design-navigation/descendant-lateral.jd b/docs/html/training/design-navigation/descendant-lateral.jd
index 0064bd4..2d97e40 100644
--- a/docs/html/training/design-navigation/descendant-lateral.jd
+++ b/docs/html/training/design-navigation/descendant-lateral.jd
@@ -117,7 +117,16 @@
 <p class="img-caption"><strong>Figure 5.</strong> Example phone and tablet tab-based navigation interfaces with relevant screen map excerpt.</p>
 
 
-<p>Several best practices apply when using tabs. Tabs should be persistent across immediate related screens. Only the designated content region should change when selecting a tab, and tab indicators should remain available at all times. Additionally, tab switches should not be treated as history. For example, if a user switches from a tab <em>A</em> to another tab <em>B</em>, pressing the BACK button (more on that in the <a href="ancestral-temporal.html">next lesson</a>) should not re-select tab <em>A</em>. Tabs are usually laid out horizontally, although other presentations of tab navigation such as using a drop-down list in the Action Bar (<a href="{@docRoot}design/patterns/actionbar.html">pattern docs</a> at Android Design) are sometimes appropriate. Lastly, and most importantly, <em>tabs should always run along the top of the screen</em>, and should not be aligned to the bottom of the screen.</p>
+<p>Several best practices apply when using tabs. Tabs should be persistent across immediate related
+screens. Only the designated content region should change when selecting a tab, and tab indicators
+should remain available at all times. Additionally, tab switches should not be treated as history.
+For example, if a user switches from a tab <em>A</em> to another tab <em>B</em>, pressing the
+<em>Back</em> button (more on that in the <a href="ancestral-temporal.html">next lesson</a>) should
+not re-select tab <em>A</em>. Tabs are usually laid out horizontally, although other presentations
+of tab navigation such as using a drop-down list in the Action Bar (<a
+href="{@docRoot}design/patterns/actionbar.html">pattern docs</a> at Android Design) are sometimes
+appropriate. Lastly, and most importantly, <em>tabs should always run along the top of the
+screen</em>, and should not be aligned to the bottom of the screen.</p>
 
 <p>There are some obvious immediate benefits of tabs over simpler list- and button-based navigation:</p>
 
diff --git a/docs/html/training/design-navigation/index.jd b/docs/html/training/design-navigation/index.jd
index cb20a60..af60717 100644
--- a/docs/html/training/design-navigation/index.jd
+++ b/docs/html/training/design-navigation/index.jd
@@ -41,7 +41,9 @@
     <dd>Learn about techniques for allowing users to navigate deep into, as well as across, your content hierarchy. Also learn about pros and cons of, and best practices for, specific navigational UI elements for various situations.</dd>
 
   <dt><strong><a href="ancestral-temporal.html">Providing Ancestral and Temporal Navigation</a></strong></dt>
-    <dd>Learn how to allow users to navigate upwards in the content hierarchy. Also learn about best practices for the BACK button and temporal navigation, or navigation to previous screens that may not be hierarchically related.</dd>
+    <dd>Learn how to allow users to navigate upwards in the content hierarchy. Also learn about best
+practices for the <em>Back</em> button and temporal navigation, or navigation to previous screens
+that may not be hierarchically related.</dd>
 
   <dt><strong><a href="wireframing.html">Putting it All Together: Wireframing the Example App</a></strong></dt>
     <dd>Learn how to create screen wireframes (low-fidelity graphic mockups) representing the screens in a news application based on the desired information model. These wireframes utilize navigational elements discussed in previous lessons to demonstrate intuitive and efficient navigation.</dd>
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index f285f5b..11c2427 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -487,6 +487,7 @@
 
         final byte[] data = fp.getData();
         int eSize = mType.mElement.mElements[component_number].getSizeBytes();
+        eSize *= mType.mElement.mArraySizes[component_number];
 
         if (data.length != eSize) {
             throw new RSIllegalArgumentException("Field packer sizelength " + data.length +
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index ad10832..bfe412c 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2008-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.
@@ -837,7 +837,8 @@
                         mRS.mErrorCallback.mErrorNum = subID;
                         mRS.mErrorCallback.run();
                     } else {
-                        //throw new RSRuntimeException("Received error num " + subID + ", details: " + e);
+                        // Do not throw here. In these cases, we do not have
+                        // a fatal error.
                     }
                     continue;
                 }
diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h
index 3378d97..691ba2f 100644
--- a/include/binder/IPCThreadState.h
+++ b/include/binder/IPCThreadState.h
@@ -41,6 +41,7 @@
 
             int                 getCallingPid();
             int                 getCallingUid();
+            int                 getOrigCallingUid();
 
             void                setStrictModePolicy(int32_t policy);
             int32_t             getStrictModePolicy() const;
@@ -116,6 +117,7 @@
             status_t            mLastError;
             pid_t               mCallingPid;
             uid_t               mCallingUid;
+            uid_t               mOrigCallingUid;
             int32_t             mStrictModePolicy;
             int32_t             mLastTransactionBinderFlags;
 };
diff --git a/include/gui/DisplayEventReceiver.h b/include/gui/DisplayEventReceiver.h
index a281377..7bca8d6 100644
--- a/include/gui/DisplayEventReceiver.h
+++ b/include/gui/DisplayEventReceiver.h
@@ -95,6 +95,8 @@
      * should be destroyed and getEvents() shouldn't be called again.
      */
     ssize_t getEvents(Event* events, size_t count);
+    static ssize_t getEvents(const sp<BitTube>& dataChannel,
+            Event* events, size_t count);
 
     /*
      * setVsyncRate() sets the Event::VSync delivery rate. A value of
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h
index 1417416..7b0b443 100644
--- a/include/media/AudioEffect.h
+++ b/include/media/AudioEffect.h
@@ -226,8 +226,8 @@
     AudioEffect(const effect_uuid_t *type,
                 const effect_uuid_t *uuid = NULL,
                   int32_t priority = 0,
-                  effect_callback_t cbf = 0,
-                  void* user = 0,
+                  effect_callback_t cbf = NULL,
+                  void* user = NULL,
                   int sessionId = 0,
                   audio_io_handle_t io = 0
                   );
@@ -238,8 +238,8 @@
     AudioEffect(const char *typeStr,
                     const char *uuidStr = NULL,
                     int32_t priority = 0,
-                    effect_callback_t cbf = 0,
-                    void* user = 0,
+                    effect_callback_t cbf = NULL,
+                    void* user = NULL,
                     int sessionId = 0,
                     audio_io_handle_t io = 0
                     );
@@ -260,8 +260,8 @@
             status_t    set(const effect_uuid_t *type,
                             const effect_uuid_t *uuid = NULL,
                             int32_t priority = 0,
-                            effect_callback_t cbf = 0,
-                            void* user = 0,
+                            effect_callback_t cbf = NULL,
+                            void* user = NULL,
                             int sessionId = 0,
                             audio_io_handle_t io = 0
                             );
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 76ec3b1..c8c5dba 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -155,8 +155,8 @@
                                     uint32_t channelMask = AUDIO_CHANNEL_IN_MONO,
                                     int frameCount      = 0,
                                     uint32_t flags      = 0,
-                                    callback_t cbf = 0,
-                                    void* user = 0,
+                                    callback_t cbf = NULL,
+                                    void* user = NULL,
                                     int notificationFrames = 0,
                                     int sessionId = 0);
 
@@ -181,8 +181,8 @@
                             uint32_t channelMask = AUDIO_CHANNEL_IN_MONO,
                             int frameCount      = 0,
                             uint32_t flags      = 0,
-                            callback_t cbf = 0,
-                            void* user = 0,
+                            callback_t cbf = NULL,
+                            void* user = NULL,
                             int notificationFrames = 0,
                             bool threadCanCallJava = false,
                             int sessionId = 0);
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 6b12c14..74a1e62 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -248,7 +248,7 @@
     static sp<IAudioPolicyService> gAudioPolicyService;
 
     // mapping between stream types and outputs
-    static DefaultKeyedVector<int, audio_io_handle_t> gStreamOutputMap;
+    static DefaultKeyedVector<audio_stream_type_t, audio_io_handle_t> gStreamOutputMap;
     // list of output descriptors containing cached parameters
     // (sampling rate, framecount, channel count...)
     static DefaultKeyedVector<audio_io_handle_t, OutputDescriptor *> gOutputs;
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 98abfbd..02c85cd 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -148,8 +148,8 @@
                                     int channelMask      = 0,
                                     int frameCount       = 0,
                                     uint32_t flags       = 0,
-                                    callback_t cbf       = 0,
-                                    void* user           = 0,
+                                    callback_t cbf       = NULL,
+                                    void* user           = NULL,
                                     int notificationFrames = 0,
                                     int sessionId = 0);
 
@@ -180,8 +180,8 @@
                                     int channelMask     = 0,
                                     const sp<IMemory>& sharedBuffer = 0,
                                     uint32_t flags      = 0,
-                                    callback_t cbf      = 0,
-                                    void* user          = 0,
+                                    callback_t cbf      = NULL,
+                                    void* user          = NULL,
                                     int notificationFrames = 0,
                                     int sessionId = 0);
 
@@ -204,8 +204,8 @@
                             int channelMask     = 0,
                             int frameCount      = 0,
                             uint32_t flags      = 0,
-                            callback_t cbf      = 0,
-                            void* user          = 0,
+                            callback_t cbf      = NULL,
+                            void* user          = NULL,
                             int notificationFrames = 0,
                             const sp<IMemory>& sharedBuffer = 0,
                             bool threadCanCallJava = false,
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 7c0d886..760595c 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -126,7 +126,7 @@
                                     uint32_t *pSamplingRate,
                                     audio_format_t *pFormat,
                                     uint32_t *pChannels,
-                                    uint32_t acoustics) = 0;
+                                    audio_in_acoustics_t acoustics) = 0;
     virtual status_t closeInput(int input) = 0;
 
     virtual status_t setStreamOutput(audio_stream_type_t stream, int output) = 0;
diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h
index 7d890bd..df0c97e 100644
--- a/include/media/ToneGenerator.h
+++ b/include/media/ToneGenerator.h
@@ -154,7 +154,7 @@
     ToneGenerator(audio_stream_type_t streamType, float volume, bool threadCanCallJava = false);
     ~ToneGenerator();
 
-    bool startTone(int toneType, int durationMs = -1);
+    bool startTone(tone_type toneType, int durationMs = -1);
     void stopTone();
 
     bool isInited() { return (mState == TONE_IDLE)?false:true;}
@@ -274,7 +274,7 @@
     bool prepareWave();
     unsigned int numWaves(unsigned int segmentIdx);
     void clearWaveGens();
-    int getToneForRegion(int toneType);
+    tone_type getToneForRegion(tone_type toneType);
 
     // WaveGenerator generates a single sine wave
     class WaveGenerator {
diff --git a/include/media/Visualizer.h b/include/media/Visualizer.h
index 1a4cbca..60fa15b 100644
--- a/include/media/Visualizer.h
+++ b/include/media/Visualizer.h
@@ -66,8 +66,8 @@
      * See AudioEffect constructor for details on parameters.
      */
                         Visualizer(int32_t priority = 0,
-                                   effect_callback_t cbf = 0,
-                                   void* user = 0,
+                                   effect_callback_t cbf = NULL,
+                                   void* user = NULL,
                                    int sessionId = 0);
 
                         ~Visualizer();
diff --git a/include/media/stagefright/AACWriter.h b/include/media/stagefright/AACWriter.h
index fa3ab8a..49397ee 100644
--- a/include/media/stagefright/AACWriter.h
+++ b/include/media/stagefright/AACWriter.h
@@ -34,7 +34,7 @@
     virtual status_t addSource(const sp<MediaSource> &source);
     virtual bool reachedEOS();
     virtual status_t start(MetaData *params = NULL);
-    virtual status_t stop();
+    virtual status_t stop() { return reset(); }
     virtual status_t pause();
 
 protected:
@@ -66,6 +66,7 @@
     bool exceedsFileSizeLimit();
     bool exceedsFileDurationLimit();
     status_t writeAdtsHeader(uint32_t frameLength);
+    status_t reset();
 
     DISALLOW_EVIL_CONSTRUCTORS(AACWriter);
 };
diff --git a/include/media/stagefright/AMRWriter.h b/include/media/stagefright/AMRWriter.h
index 62d57b4..392f968 100644
--- a/include/media/stagefright/AMRWriter.h
+++ b/include/media/stagefright/AMRWriter.h
@@ -37,7 +37,7 @@
     virtual status_t addSource(const sp<MediaSource> &source);
     virtual bool reachedEOS();
     virtual status_t start(MetaData *params = NULL);
-    virtual status_t stop();
+    virtual status_t stop() { return reset(); }
     virtual status_t pause();
 
 protected:
@@ -60,6 +60,7 @@
     status_t threadFunc();
     bool exceedsFileSizeLimit();
     bool exceedsFileDurationLimit();
+    status_t reset();
 
     AMRWriter(const AMRWriter &);
     AMRWriter &operator=(const AMRWriter &);
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index 2427e2f..79437bf 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -40,7 +40,7 @@
     status_t initCheck() const;
 
     virtual status_t start(MetaData *params = NULL);
-    virtual status_t stop();
+    virtual status_t stop() { return reset(); }
     virtual sp<MetaData> getFormat();
 
     // Returns the maximum amplitude since last call.
@@ -97,6 +97,7 @@
 
     void releaseQueuedFrames_l();
     void waitOutstandingEncodingFrames_l();
+    status_t reset();
 
     AudioSource(const AudioSource &);
     AudioSource &operator=(const AudioSource &);
diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
index 446720b..5a35358 100644
--- a/include/media/stagefright/CameraSource.h
+++ b/include/media/stagefright/CameraSource.h
@@ -79,7 +79,7 @@
     virtual ~CameraSource();
 
     virtual status_t start(MetaData *params = NULL);
-    virtual status_t stop();
+    virtual status_t stop() { return reset(); }
     virtual status_t read(
             MediaBuffer **buffer, const ReadOptions *options = NULL);
 
@@ -163,7 +163,6 @@
                  bool storeMetaDataInVideoBuffers);
 
     virtual void startCameraRecording();
-    virtual void stopCameraRecording();
     virtual void releaseRecordingFrame(const sp<IMemory>& frame);
 
     // Returns true if need to skip the current frame.
@@ -220,7 +219,9 @@
     status_t checkFrameRate(const CameraParameters& params,
                     int32_t frameRate);
 
+    void stopCameraRecording();
     void releaseCamera();
+    status_t reset();
 
     CameraSource(const CameraSource &);
     CameraSource &operator=(const CameraSource &);
diff --git a/include/media/stagefright/CameraSourceTimeLapse.h b/include/media/stagefright/CameraSourceTimeLapse.h
index b060691..0936da2 100644
--- a/include/media/stagefright/CameraSourceTimeLapse.h
+++ b/include/media/stagefright/CameraSourceTimeLapse.h
@@ -121,9 +121,6 @@
     // Wrapper over CameraSource::read() to implement quick stop.
     virtual status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL);
 
-    // For video camera case, just stops the camera's video recording.
-    virtual void stopCameraRecording();
-
     // mSkipCurrentFrame is set to true in dataCallbackTimestamp() if the current
     // frame needs to be skipped and this function just returns the value of mSkipCurrentFrame.
     virtual bool skipCurrentFrame(int64_t timestampUs);
diff --git a/include/media/stagefright/MPEG2TSWriter.h b/include/media/stagefright/MPEG2TSWriter.h
index e4c1c49..a7c9ecf 100644
--- a/include/media/stagefright/MPEG2TSWriter.h
+++ b/include/media/stagefright/MPEG2TSWriter.h
@@ -37,7 +37,7 @@
 
     virtual status_t addSource(const sp<MediaSource> &source);
     virtual status_t start(MetaData *param = NULL);
-    virtual status_t stop();
+    virtual status_t stop() { return reset(); }
     virtual status_t pause();
     virtual bool reachedEOS();
     virtual status_t dump(int fd, const Vector<String16>& args);
@@ -78,6 +78,7 @@
     void writeAccessUnit(int32_t sourceIndex, const sp<ABuffer> &buffer);
 
     ssize_t internalWrite(const void *data, size_t size);
+    status_t reset();
 
     DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSWriter);
 };
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 77166ed..0409b30 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -37,7 +37,7 @@
 
     virtual status_t addSource(const sp<MediaSource> &source);
     virtual status_t start(MetaData *param = NULL);
-    virtual status_t stop();
+    virtual status_t stop() { return reset(); }
     virtual status_t pause();
     virtual bool reachedEOS();
     virtual status_t dump(int fd, const Vector<String16>& args);
@@ -184,6 +184,7 @@
     void writeLongitude(int degreex10000);
     void sendSessionSummary();
     void release();
+    status_t reset();
 
     MPEG4Writer(const MPEG4Writer &);
     MPEG4Writer &operator=(const MPEG4Writer &);
diff --git a/include/media/stagefright/SurfaceMediaSource.h b/include/media/stagefright/SurfaceMediaSource.h
index d0940bb..54baab6 100644
--- a/include/media/stagefright/SurfaceMediaSource.h
+++ b/include/media/stagefright/SurfaceMediaSource.h
@@ -58,7 +58,7 @@
 
     // For the MediaSource interface for use by StageFrightRecorder:
     virtual status_t start(MetaData *params = NULL);
-    virtual status_t stop();
+    virtual status_t stop() { return reset(); }
     virtual status_t read(
             MediaBuffer **buffer, const ReadOptions *options = NULL);
     virtual sp<MetaData> getFormat();
@@ -359,6 +359,8 @@
     Condition mFrameAvailableCondition;
     Condition mFrameCompleteCondition;
 
+    status_t reset();
+
     // Avoid copying and equating and default constructor
     DISALLOW_IMPLICIT_CONSTRUCTORS(SurfaceMediaSource);
 };
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index ffc546e..dd97ce4 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -76,7 +76,9 @@
                 // Left channel is in [0:15], right channel is in [16:31].
                 // Always read and write the combined pair atomically.
                 // For AudioTrack only, not used by AudioRecord.
-                uint32_t    volumeLR;
+private:
+                uint32_t    mVolumeLR;
+public:
 
                 uint32_t    sampleRate;
                 // NOTE: audio_track_cblk_t::frameSize is not equal to AudioTrack::frameSize() for
@@ -116,6 +118,17 @@
                 uint16_t    getSendLevel_U4_12() const {
                     return mSendLevel;
                 }
+
+                // for AudioTrack client only, caller must limit to 0 <= volumeLR <= 0x10001000
+                void        setVolumeLR(uint32_t volumeLR) {
+                    mVolumeLR = volumeLR;
+                }
+
+                // for AudioFlinger only; the return value must be validated by the caller
+                uint32_t    getVolumeLR() const {
+                    return mVolumeLR;
+                }
+
 };
 
 
diff --git a/include/utils/threads.h b/include/utils/threads.h
index ab3e8cd..b4a8b7c 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -526,6 +526,12 @@
     // Do not call from this object's thread; will return WOULD_BLOCK in that case.
             status_t    join();
 
+#ifdef HAVE_ANDROID_OS
+    // Return the thread's kernel ID, same as the thread itself calling gettid() or
+    // androidGetTid(), or -1 if the thread is not running.
+            pid_t       getTid() const;
+#endif
+
 protected:
     // exitPending() returns true if requestExit() has been called.
             bool        exitPending() const;
@@ -551,8 +557,10 @@
     volatile bool           mExitPending;
     volatile bool           mRunning;
             sp<Thread>      mHoldSelf;
-#if HAVE_ANDROID_OS
-            int             mTid;
+#ifdef HAVE_ANDROID_OS
+    // legacy for debugging, not used by getTid() as it is set by the child thread
+    // and so is not initialized until the child reaches that point
+            pid_t           mTid;
 #endif
 };
 
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 629b899..b578a6c 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -371,6 +371,11 @@
     return mCallingUid;
 }
 
+int IPCThreadState::getOrigCallingUid()
+{
+    return mOrigCallingUid;
+}
+
 int64_t IPCThreadState::clearCallingIdentity()
 {
     int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
@@ -641,6 +646,7 @@
 {
     pthread_setspecific(gTLS, this);
     clearCaller();
+    mOrigCallingUid = mCallingUid;
     mIn.setDataCapacity(256);
     mOut.setDataCapacity(256);
 }
@@ -987,6 +993,7 @@
             
             mCallingPid = tr.sender_pid;
             mCallingUid = tr.sender_euid;
+            mOrigCallingUid = tr.sender_euid;
             
             int curPrio = getpriority(PRIO_PROCESS, mMyThreadId);
             if (gDisableBackgroundScheduling) {
@@ -1045,6 +1052,7 @@
             
             mCallingPid = origPid;
             mCallingUid = origUid;
+            mOrigCallingUid = origUid;
 
             IF_LOG_TRANSACTIONS() {
                 TextOutput::Bundle _b(alog);
diff --git a/libs/gui/BitTube.cpp b/libs/gui/BitTube.cpp
index 785da39..55f4178 100644
--- a/libs/gui/BitTube.cpp
+++ b/libs/gui/BitTube.cpp
@@ -17,8 +17,9 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <unistd.h>
 #include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
 
 #include <utils/Errors.h>
 
@@ -38,6 +39,8 @@
         mSendFd = fds[1];
         fcntl(mReceiveFd, F_SETFL, O_NONBLOCK);
         fcntl(mSendFd, F_SETFL, O_NONBLOCK);
+        // ignore SIGPIPE, we handle write errors through EPIPE instead
+        signal(SIGPIPE, SIG_IGN);
     } else {
         mReceiveFd = -errno;
         ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd));
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 3b3ccaa..6a4763d 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -80,7 +80,13 @@
 
 ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events,
         size_t count) {
-    ssize_t size = mDataChannel->read(events, sizeof(events[0])*count);
+    return DisplayEventReceiver::getEvents(mDataChannel, events, count);
+}
+
+ssize_t DisplayEventReceiver::getEvents(const sp<BitTube>& dataChannel,
+        Event* events, size_t count)
+{
+    ssize_t size = dataChannel->read(events, sizeof(events[0])*count);
     ALOGE_IF(size<0,
             "DisplayEventReceiver::getEvents error (%s)",
             strerror(-size));
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 95e0a18..5ec3983 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -26,6 +26,7 @@
 		ShapeCache.cpp \
 		SkiaColorFilter.cpp \
 		SkiaShader.cpp \
+		Snapshot.cpp \
 		TextureCache.cpp \
 		TextDropShadowCache.cpp
 	
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 0ad0c2a..16a3d73 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -62,6 +62,9 @@
 // Turn on to display debug info about the layer renderer
 #define DEBUG_LAYER_RENDERER 0
 
+// Turn on to enable additional debugging in the font renderers
+#define DEBUG_FONT_RENDERER 0
+
 // Turn on to dump display list state
 #define DEBUG_DISPLAY_LIST 0
 
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 74efda2..3df105b 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -151,10 +151,12 @@
     int32_t bX = 0, bY = 0;
     for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
         for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
+#if DEBUG_FONT_RENDERER
             if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
                 ALOGE("Skipping invalid index");
                 continue;
             }
+#endif
             uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
             bitmap[bY * bitmapW + bX] = tempCol;
         }
@@ -226,7 +228,7 @@
     };
     RenderGlyph render = gRenderGlyph[mode];
 
-    if (positions == NULL) {
+    if (CC_LIKELY(positions == NULL)) {
         SkFixed prevRsbDelta = 0;
 
         float penX = x;
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 71fb8da..7854729 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -38,8 +38,8 @@
 #define LAYER_SIZE 64
 
 // Defines the size in bits of the stencil buffer
-// Note: We only want 1 bit, but in practice we'll get 8 bits on all GPUs
-//       for the foreseeable future
+// Note: Only 1 bit is required for clipping but more bits are required
+// to properly implement the winding fill rule when rasterizing paths
 #define STENCIL_BUFFER_SIZE 0
 
 /**
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
new file mode 100644
index 0000000..a85362d
--- /dev/null
+++ b/libs/hwui/Snapshot.cpp
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#include "Snapshot.h"
+
+#include <SkCanvas.h>
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors
+///////////////////////////////////////////////////////////////////////////////
+
+Snapshot::Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0),
+        invisible(false), empty(false) {
+
+    transform = &mTransformRoot;
+    clipRect = &mClipRectRoot;
+    region = NULL;
+}
+
+/**
+ * Copies the specified snapshot/ The specified snapshot is stored as
+ * the previous snapshot.
+ */
+Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags):
+        flags(0), previous(s), layer(NULL), fbo(s->fbo),
+        invisible(s->invisible), empty(false),
+        viewport(s->viewport), height(s->height) {
+
+    if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
+        mTransformRoot.load(*s->transform);
+        transform = &mTransformRoot;
+    } else {
+        transform = s->transform;
+    }
+
+    if (saveFlags & SkCanvas::kClip_SaveFlag) {
+        mClipRectRoot.set(*s->clipRect);
+        clipRect = &mClipRectRoot;
+    } else {
+        clipRect = s->clipRect;
+    }
+
+    if (s->flags & Snapshot::kFlagFboTarget) {
+        flags |= Snapshot::kFlagFboTarget;
+        region = s->region;
+    } else {
+        region = NULL;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Clipping
+///////////////////////////////////////////////////////////////////////////////
+
+bool Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) {
+    Rect r(left, top, right, bottom);
+    transform->mapRect(r);
+    return clipTransformed(r, op);
+}
+
+bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) {
+    bool clipped = false;
+
+    // NOTE: The unimplemented operations require support for regions
+    // Supporting regions would require using a stencil buffer instead
+    // of the scissor. The stencil buffer itself is not too expensive
+    // (memory cost excluded) but on fillrate limited devices, managing
+    // the stencil might have a negative impact on the framerate.
+    switch (op) {
+        case SkRegion::kDifference_Op:
+            break;
+        case SkRegion::kIntersect_Op:
+            clipped = clipRect->intersect(r);
+            if (!clipped) {
+                clipRect->setEmpty();
+                clipped = true;
+            }
+            break;
+        case SkRegion::kUnion_Op:
+            clipped = clipRect->unionWith(r);
+            break;
+        case SkRegion::kXOR_Op:
+            break;
+        case SkRegion::kReverseDifference_Op:
+            break;
+        case SkRegion::kReplace_Op:
+            clipRect->set(r);
+            clipped = true;
+            break;
+    }
+
+    if (clipped) {
+        flags |= Snapshot::kFlagClipSet;
+    }
+
+    return clipped;
+}
+
+void Snapshot::setClip(float left, float top, float right, float bottom) {
+    clipRect->set(left, top, right, bottom);
+    flags |= Snapshot::kFlagClipSet;
+}
+
+const Rect& Snapshot::getLocalClip() {
+    mat4 inverse;
+    inverse.loadInverse(*transform);
+
+    mLocalClip.set(*clipRect);
+    inverse.mapRect(mLocalClip);
+
+    return mLocalClip;
+}
+
+void Snapshot::resetClip(float left, float top, float right, float bottom) {
+    clipRect = &mClipRectRoot;
+    clipRect->set(left, top, right, bottom);
+    flags |= Snapshot::kFlagClipSet;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Transforms
+///////////////////////////////////////////////////////////////////////////////
+
+void Snapshot::resetTransform(float x, float y, float z) {
+    transform = &mTransformRoot;
+    transform->loadTranslate(x, y, z);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Queries
+///////////////////////////////////////////////////////////////////////////////
+
+bool Snapshot::isIgnored() const {
+    return invisible || empty;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index aff7b93..c94af7e 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -23,7 +23,7 @@
 #include <utils/RefBase.h>
 #include <ui/Region.h>
 
-#include <SkCanvas.h>
+#include <SkRegion.h>
 
 #include "Layer.h"
 #include "Matrix.h"
@@ -43,43 +43,12 @@
  */
 class Snapshot: public LightRefBase<Snapshot> {
 public:
-    Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0), invisible(false), empty(false) {
-        transform = &mTransformRoot;
-        clipRect = &mClipRectRoot;
-        region = NULL;
-    }
+
+    Snapshot();
+    Snapshot(const sp<Snapshot>& s, int saveFlags);
 
     /**
-     * Copies the specified snapshot/ The specified snapshot is stored as
-     * the previous snapshot.
-     */
-    Snapshot(const sp<Snapshot>& s, int saveFlags):
-            flags(0), previous(s), layer(NULL), fbo(s->fbo),
-            invisible(s->invisible), empty(false), viewport(s->viewport), height(s->height) {
-        if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
-            mTransformRoot.load(*s->transform);
-            transform = &mTransformRoot;
-        } else {
-            transform = s->transform;
-        }
-
-        if (saveFlags & SkCanvas::kClip_SaveFlag) {
-            mClipRectRoot.set(*s->clipRect);
-            clipRect = &mClipRectRoot;
-        } else {
-            clipRect = s->clipRect;
-        }
-
-        if (s->flags & Snapshot::kFlagFboTarget) {
-            flags |= Snapshot::kFlagFboTarget;
-            region = s->region;
-        } else {
-            region = NULL;
-        }
-    }
-
-    /**
-     * Various flags set on #flags.
+     * Various flags set on ::flags.
      */
     enum Flags {
         /**
@@ -115,87 +84,41 @@
      * by this snapshot's trasnformation.
      */
     bool clip(float left, float top, float right, float bottom,
-            SkRegion::Op op = SkRegion::kIntersect_Op) {
-        Rect r(left, top, right, bottom);
-        transform->mapRect(r);
-        return clipTransformed(r, op);
-    }
+            SkRegion::Op op = SkRegion::kIntersect_Op);
 
     /**
      * Modifies the current clip with the new clip rectangle and
      * the specified operation. The specified rectangle is considered
      * already transformed.
      */
-    bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op) {
-        bool clipped = false;
-
-        // NOTE: The unimplemented operations require support for regions
-        // Supporting regions would require using a stencil buffer instead
-        // of the scissor. The stencil buffer itself is not too expensive
-        // (memory cost excluded) but on fillrate limited devices, managing
-        // the stencil might have a negative impact on the framerate.
-        switch (op) {
-            case SkRegion::kDifference_Op:
-                break;
-            case SkRegion::kIntersect_Op:
-                clipped = clipRect->intersect(r);
-                if (!clipped) {
-                    clipRect->setEmpty();
-                    clipped = true;
-                }
-                break;
-            case SkRegion::kUnion_Op:
-                clipped = clipRect->unionWith(r);
-                break;
-            case SkRegion::kXOR_Op:
-                break;
-            case SkRegion::kReverseDifference_Op:
-                break;
-            case SkRegion::kReplace_Op:
-                clipRect->set(r);
-                clipped = true;
-                break;
-        }
-
-        if (clipped) {
-            flags |= Snapshot::kFlagClipSet;
-        }
-
-        return clipped;
-    }
+    bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op);
 
     /**
      * Sets the current clip.
      */
-    void setClip(float left, float top, float right, float bottom) {
-        clipRect->set(left, top, right, bottom);
-        flags |= Snapshot::kFlagClipSet;
-    }
+    void setClip(float left, float top, float right, float bottom);
 
-    const Rect& getLocalClip() {
-        mat4 inverse;
-        inverse.loadInverse(*transform);
+    /**
+     * Returns the current clip in local coordinates. The clip rect is
+     * transformed by the inverse transform matrix.
+     */
+    const Rect& getLocalClip();
 
-        mLocalClip.set(*clipRect);
-        inverse.mapRect(mLocalClip);
+    /**
+     * Resets the clip to the specified rect.
+     */
+    void resetClip(float left, float top, float right, float bottom);
 
-        return mLocalClip;
-    }
+    /**
+     * Resets the current transform to a pure 3D translation.
+     */
+    void resetTransform(float x, float y, float z);
 
-    void resetTransform(float x, float y, float z) {
-        transform = &mTransformRoot;
-        transform->loadTranslate(x, y, z);
-    }
-
-    void resetClip(float left, float top, float right, float bottom) {
-        clipRect = &mClipRectRoot;
-        clipRect->set(left, top, right, bottom);
-        flags |= Snapshot::kFlagClipSet;
-    }
-
-    bool isIgnored() const {
-        return invisible || empty;
-    }
+    /**
+     * Indicates whether this snapshot should be ignored. A snapshot
+     * is typicalled ignored if its layer is invisible or empty.
+     */
+    bool isIgnored() const;
 
     /**
      * Dirty flags.
@@ -209,6 +132,8 @@
 
     /**
      * Only set when the flag kFlagIsLayer is set.
+     *
+     * This snapshot does not own the layer, this pointer must not be freed.
      */
     Layer* layer;
 
@@ -249,17 +174,26 @@
     /**
      * Local transformation. Holds the current translation, scale and
      * rotation values.
+     *
+     * This is a reference to a matrix owned by this snapshot or another
+     *  snapshot. This pointer must not be freed. See ::mTransformRoot.
      */
     mat4* transform;
 
     /**
      * Current clip region. The clip is stored in canvas-space coordinates,
      * (screen-space coordinates in the regular case.)
+     *
+     * This is a reference to a rect owned by this snapshot or another
+     * snapshot. This pointer must not be freed. See ::mClipRectRoot.
      */
     Rect* clipRect;
 
     /**
      * The ancestor layer's dirty region.
+     *
+     * This is a reference to a region owned by a layer. This pointer must
+     * not be freed.
      */
     Region* region;
 
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index 972e3d6..fd85b07 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -124,7 +124,8 @@
     }
 
     const Element * e = mHal.state.type->getElement()->getField(cIdx);
-    if (sizeBytes != e->getSizeBytes()) {
+    uint32_t elemArraySize = mHal.state.type->getElement()->getFieldArraySize(cIdx);
+    if (sizeBytes != e->getSizeBytes() * elemArraySize) {
         ALOGE("Error Allocation::subElementData data size %zu does not match field size %zu.", sizeBytes, e->getSizeBytes());
         rsc->setError(RS_ERROR_BAD_VALUE, "subElementData bad size.");
         return;
@@ -157,8 +158,8 @@
     }
 
     const Element * e = mHal.state.type->getElement()->getField(cIdx);
-
-    if (sizeBytes != e->getSizeBytes()) {
+    uint32_t elemArraySize = mHal.state.type->getElement()->getFieldArraySize(cIdx);
+    if (sizeBytes != e->getSizeBytes() * elemArraySize) {
         ALOGE("Error Allocation::subElementData data size %zu does not match field size %zu.", sizeBytes, e->getSizeBytes());
         rsc->setError(RS_ERROR_BAD_VALUE, "subElementData bad size.");
         return;
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index 929dd68..b4eb995 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -322,7 +322,7 @@
 
     if (!s->runCompiler(rsc, resName, cacheDir, (uint8_t *)text, text_length)) {
         // Error during compile, destroy s and return null.
-        delete s;
+        ObjectBase::checkDelete(s);
         return NULL;
     }
 
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 544ab74..24cf504 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -98,7 +98,8 @@
 
 LOCAL_C_INCLUDES += \
 		external/zlib \
-		external/icu4c/common
+		external/icu4c/common \
+		bionic/libc/private
 
 LOCAL_LDLIBS += -lpthread
 
@@ -114,7 +115,10 @@
 
 ifeq ($(TARGET_OS),linux)
 include $(CLEAR_VARS)
-LOCAL_C_INCLUDES += external/zlib external/icu4c/common
+LOCAL_C_INCLUDES += \
+		external/zlib \
+		external/icu4c/common \
+		bionic/libc/private
 LOCAL_LDLIBS := -lrt -ldl -lpthread
 LOCAL_MODULE := libutils
 LOCAL_SRC_FILES := $(commonSources) BackupData.cpp BackupHelpers.cpp
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index e343c62..ab207f5 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -34,6 +34,9 @@
 # include <pthread.h>
 # include <sched.h>
 # include <sys/resource.h>
+#ifdef HAVE_ANDROID_OS
+# include <bionic_pthread.h>
+#endif
 #elif defined(HAVE_WIN32_THREADS)
 # include <windows.h>
 # include <stdint.h>
@@ -86,7 +89,7 @@
     char *          threadName;
 
     // we use this trampoline when we need to set the priority with
-    // nice/setpriority.
+    // nice/setpriority, and name with prctl.
     static int trampoline(const thread_data_t* t) {
         thread_func_t f = t->entryFunction;
         void* u = t->userData;
@@ -141,8 +144,13 @@
 
 #ifdef HAVE_ANDROID_OS  /* valgrind is rejecting RT-priority create reqs */
     if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) {
-        // We could avoid the trampoline if there was a way to get to the
-        // android_thread_id_t (pid) from pthread_t
+        // Now that the pthread_t has a method to find the associated
+        // android_thread_id_t (pid) from pthread_t, it would be possible to avoid
+        // this trampoline in some cases as the parent could set the properties
+        // for the child.  However, there would be a race condition because the
+        // child becomes ready immediately, and it doesn't work for the name.
+        // prctl(PR_SET_NAME) only works for self; prctl(PR_SET_THREAD_NAME) was
+        // proposed but not yet accepted.
         thread_data_t* t = new thread_data_t;
         t->priority = threadPriority;
         t->threadName = threadName ? strdup(threadName) : NULL;
@@ -178,6 +186,13 @@
     return 1;
 }
 
+#ifdef HAVE_ANDROID_OS
+static pthread_t android_thread_id_t_to_pthread(android_thread_id_t thread)
+{
+    return (pthread_t) thread;
+}
+#endif
+
 android_thread_id_t androidGetThreadId()
 {
     return (android_thread_id_t)pthread_self();
@@ -909,6 +924,23 @@
     return mStatus;
 }
 
+#ifdef HAVE_ANDROID_OS
+pid_t Thread::getTid() const
+{
+    // mTid is not defined until the child initializes it, and the caller may need it earlier
+    Mutex::Autolock _l(mLock);
+    pid_t tid;
+    if (mRunning) {
+        pthread_t pthread = android_thread_id_t_to_pthread(mThread);
+        tid = __pthread_gettid(pthread);
+    } else {
+        ALOGW("Thread (this=%p): getTid() is undefined before run()", this);
+        tid = -1;
+    }
+    return tid;
+}
+#endif
+
 bool Thread::exitPending() const
 {
     Mutex::Autolock _l(mLock);
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index a720c0a..85d99c1 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -204,18 +204,24 @@
         /** MPEG4 media file format*/
         public static final int MPEG_4 = 2;
 
-        /** The following formats are audio only .aac or .amr formats **/
-        /** @deprecated  Deprecated in favor of AMR_NB */
-        /** Deprecated in favor of MediaRecorder.OutputFormat.AMR_NB */
-        /** AMR NB file format */
+        /** The following formats are audio only .aac or .amr formats */
+
+        /**
+         * AMR NB file format
+         * @deprecated  Deprecated in favor of MediaRecorder.OutputFormat.AMR_NB
+         */
         public static final int RAW_AMR = 3;
+
         /** AMR NB file format */
         public static final int AMR_NB = 3;
+
         /** AMR WB file format */
         public static final int AMR_WB = 4;
+
         /** @hide AAC ADIF file format */
         public static final int AAC_ADIF = 5;
-        /** @hide AAC ADTS file format */
+
+        /** AAC ADTS file format */
         public static final int AAC_ADTS = 6;
 
         /** @hide Stream over a socket, limited to a single stream */
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index 6639d06..a242846 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -342,7 +342,7 @@
 {
     ALOGW("IEffect died");
     mStatus = NO_INIT;
-    if (mCbf) {
+    if (mCbf != NULL) {
         status_t status = DEAD_OBJECT;
         mCbf(EVENT_ERROR, mUserData, &status);
     }
@@ -363,7 +363,7 @@
             mStatus = ALREADY_EXISTS;
         }
     }
-    if (mCbf) {
+    if (mCbf != NULL) {
         mCbf(EVENT_CONTROL_STATUS_CHANGED, mUserData, &controlGranted);
     }
 }
@@ -373,7 +373,7 @@
     ALOGV("enableStatusChanged %p enabled %d mCbf %p", this, enabled, mCbf);
     if (mStatus == ALREADY_EXISTS) {
         mEnabled = enabled;
-        if (mCbf) {
+        if (mCbf != NULL) {
             mCbf(EVENT_ENABLE_STATUS_CHANGED, mUserData, &enabled);
         }
     }
@@ -389,7 +389,7 @@
         return;
     }
 
-    if (mCbf && cmdCode == EFFECT_CMD_SET_PARAM) {
+    if (mCbf != NULL && cmdCode == EFFECT_CMD_SET_PARAM) {
         effect_param_t *cmd = (effect_param_t *)cmdData;
         cmd->status = *(int32_t *)replyData;
         mCbf(EVENT_PARAMETER_CHANGED, mUserData, cmd);
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 2b3ea38..c96bc76 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -206,7 +206,7 @@
         return status;
     }
 
-    if (cbf != 0) {
+    if (cbf != NULL) {
         mClientRecordThread = new ClientRecordThread(*this, threadCanCallJava);
     }
 
@@ -387,7 +387,7 @@
 
 status_t AudioRecord::setMarkerPosition(uint32_t marker)
 {
-    if (mCbf == 0) return INVALID_OPERATION;
+    if (mCbf == NULL) return INVALID_OPERATION;
 
     mMarkerPosition = marker;
     mMarkerReached = false;
@@ -397,7 +397,7 @@
 
 status_t AudioRecord::getMarkerPosition(uint32_t *marker)
 {
-    if (marker == 0) return BAD_VALUE;
+    if (marker == NULL) return BAD_VALUE;
 
     *marker = mMarkerPosition;
 
@@ -406,7 +406,7 @@
 
 status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod)
 {
-    if (mCbf == 0) return INVALID_OPERATION;
+    if (mCbf == NULL) return INVALID_OPERATION;
 
     uint32_t curPosition;
     getPosition(&curPosition);
@@ -418,7 +418,7 @@
 
 status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod)
 {
-    if (updatePeriod == 0) return BAD_VALUE;
+    if (updatePeriod == NULL) return BAD_VALUE;
 
     *updatePeriod = mUpdatePeriod;
 
@@ -427,7 +427,7 @@
 
 status_t AudioRecord::getPosition(uint32_t *position)
 {
-    if (position == 0) return BAD_VALUE;
+    if (position == NULL) return BAD_VALUE;
 
     AutoMutex lock(mLock);
     *position = mCblk->user;
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 124032b..110a294 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -35,7 +35,8 @@
 sp<AudioSystem::AudioFlingerClient> AudioSystem::gAudioFlingerClient;
 audio_error_callback AudioSystem::gAudioErrorCallback = NULL;
 // Cached values
-DefaultKeyedVector<int, audio_io_handle_t> AudioSystem::gStreamOutputMap(0);
+
+DefaultKeyedVector<audio_stream_type_t, audio_io_handle_t> AudioSystem::gStreamOutputMap(0);
 DefaultKeyedVector<audio_io_handle_t, AudioSystem::OutputDescriptor *> AudioSystem::gOutputs(0);
 
 // Cached values for recording queries, all protected by gLock
@@ -224,7 +225,7 @@
 
     gLock.lock();
     outputDesc = AudioSystem::gOutputs.valueFor(output);
-    if (outputDesc == 0) {
+    if (outputDesc == NULL) {
         ALOGV("getOutputSamplingRate() no output descriptor for output %d in gOutputs", output);
         gLock.unlock();
         const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
@@ -262,7 +263,7 @@
 
     gLock.lock();
     outputDesc = AudioSystem::gOutputs.valueFor(output);
-    if (outputDesc == 0) {
+    if (outputDesc == NULL) {
         gLock.unlock();
         const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
         if (af == 0) return PERMISSION_DENIED;
@@ -293,7 +294,7 @@
 
     gLock.lock();
     outputDesc = AudioSystem::gOutputs.valueFor(output);
-    if (outputDesc == 0) {
+    if (outputDesc == NULL) {
         gLock.unlock();
         const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
         if (af == 0) return PERMISSION_DENIED;
@@ -404,7 +405,7 @@
 void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, int ioHandle, void *param2) {
     ALOGV("ioConfigChanged() event %d", event);
     OutputDescriptor *desc;
-    uint32_t stream;
+    audio_stream_type_t stream;
 
     if (ioHandle == 0) return;
 
@@ -412,8 +413,8 @@
 
     switch (event) {
     case STREAM_CONFIG_CHANGED:
-        if (param2 == 0) break;
-        stream = *(uint32_t *)param2;
+        if (param2 == NULL) break;
+        stream = *(audio_stream_type_t *)param2;
         ALOGV("ioConfigChanged() STREAM_CONFIG_CHANGED stream %d, output %d", stream, ioHandle);
         if (gStreamOutputMap.indexOfKey(stream) >= 0) {
             gStreamOutputMap.replaceValueFor(stream, ioHandle);
@@ -424,7 +425,7 @@
             ALOGV("ioConfigChanged() opening already existing output! %d", ioHandle);
             break;
         }
-        if (param2 == 0) break;
+        if (param2 == NULL) break;
         desc = (OutputDescriptor *)param2;
 
         OutputDescriptor *outputDesc =  new OutputDescriptor(*desc);
@@ -453,7 +454,7 @@
             ALOGW("ioConfigChanged() modifying unknow output! %d", ioHandle);
             break;
         }
-        if (param2 == 0) break;
+        if (param2 == NULL) break;
         desc = (OutputDescriptor *)param2;
 
         ALOGV("ioConfigChanged() new config for output %d samplingRate %d, format %d channels %d frameCount %d latency %d",
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 17e3d4b..8c33f41 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -257,7 +257,7 @@
         return status;
     }
 
-    if (cbf != 0) {
+    if (cbf != NULL) {
         mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
     }
 
@@ -501,7 +501,7 @@
     mVolume[LEFT] = left;
     mVolume[RIGHT] = right;
 
-    mCblk->volumeLR = (uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000);
+    mCblk->setVolumeLR((uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000));
 
     return NO_ERROR;
 }
@@ -604,13 +604,13 @@
 status_t AudioTrack::getLoop(uint32_t *loopStart, uint32_t *loopEnd, int *loopCount)
 {
     AutoMutex lock(mLock);
-    if (loopStart != 0) {
+    if (loopStart != NULL) {
         *loopStart = mCblk->loopStart;
     }
-    if (loopEnd != 0) {
+    if (loopEnd != NULL) {
         *loopEnd = mCblk->loopEnd;
     }
-    if (loopCount != 0) {
+    if (loopCount != NULL) {
         if (mCblk->loopCount < 0) {
             *loopCount = -1;
         } else {
@@ -623,7 +623,7 @@
 
 status_t AudioTrack::setMarkerPosition(uint32_t marker)
 {
-    if (mCbf == 0) return INVALID_OPERATION;
+    if (mCbf == NULL) return INVALID_OPERATION;
 
     mMarkerPosition = marker;
     mMarkerReached = false;
@@ -633,7 +633,7 @@
 
 status_t AudioTrack::getMarkerPosition(uint32_t *marker)
 {
-    if (marker == 0) return BAD_VALUE;
+    if (marker == NULL) return BAD_VALUE;
 
     *marker = mMarkerPosition;
 
@@ -642,7 +642,7 @@
 
 status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod)
 {
-    if (mCbf == 0) return INVALID_OPERATION;
+    if (mCbf == NULL) return INVALID_OPERATION;
 
     uint32_t curPosition;
     getPosition(&curPosition);
@@ -654,7 +654,7 @@
 
 status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod)
 {
-    if (updatePeriod == 0) return BAD_VALUE;
+    if (updatePeriod == NULL) return BAD_VALUE;
 
     *updatePeriod = mUpdatePeriod;
 
@@ -679,7 +679,7 @@
 
 status_t AudioTrack::getPosition(uint32_t *position)
 {
-    if (position == 0) return BAD_VALUE;
+    if (position == NULL) return BAD_VALUE;
     AutoMutex lock(mLock);
     *position = mFlushed ? 0 : mCblk->server;
 
@@ -837,7 +837,7 @@
         mCblk->stepUser(mCblk->frameCount);
     }
 
-    mCblk->volumeLR = (uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | uint16_t(mVolume[LEFT] * 0x1000);
+    mCblk->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | uint16_t(mVolume[LEFT] * 0x1000));
     mCblk->setSendLevel(mSendLevel);
     mAudioTrack->attachAuxEffect(mAuxEffectId);
     mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
@@ -1319,8 +1319,8 @@
 
 audio_track_cblk_t::audio_track_cblk_t()
     : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),
-    userBase(0), serverBase(0), buffers(0), frameCount(0),
-    loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0),
+    userBase(0), serverBase(0), buffers(NULL), frameCount(0),
+    loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), mVolumeLR(0x10001000),
     mSendLevel(0), flags(0)
 {
 }
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 0d442ef..fc5520f 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -432,7 +432,7 @@
                             uint32_t *pSamplingRate,
                             audio_format_t *pFormat,
                             uint32_t *pChannels,
-                            uint32_t acoustics)
+                            audio_in_acoustics_t acoustics)
     {
         Parcel data, reply;
         uint32_t devices = pDevices ? *pDevices : 0;
@@ -445,7 +445,7 @@
         data.writeInt32(samplingRate);
         data.writeInt32(format);
         data.writeInt32(channels);
-        data.writeInt32(acoustics);
+        data.writeInt32((int32_t) acoustics);
         remote()->transact(OPEN_INPUT, data, &reply);
         int input = reply.readInt32();
         devices = reply.readInt32();
@@ -640,7 +640,7 @@
                 *id = tmp;
             }
             tmp = reply.readInt32();
-            if (enabled) {
+            if (enabled != NULL) {
                 *enabled = tmp;
             }
             effect = interface_cast<IEffect>(reply.readStrongBinder());
@@ -881,13 +881,13 @@
             uint32_t samplingRate = data.readInt32();
             audio_format_t format = (audio_format_t) data.readInt32();
             uint32_t channels = data.readInt32();
-            uint32_t acoutics = data.readInt32();
+            audio_in_acoustics_t acoustics = (audio_in_acoustics_t) data.readInt32();
 
             int input = openInput(&devices,
                                      &samplingRate,
                                      &format,
                                      &channels,
-                                     acoutics);
+                                     acoustics);
             reply->writeInt32(input);
             reply->writeInt32(devices);
             reply->writeInt32(samplingRate);
diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp
index 5a3f250..9458bc0 100644
--- a/media/libmedia/IAudioFlingerClient.cpp
+++ b/media/libmedia/IAudioFlingerClient.cpp
@@ -73,7 +73,7 @@
             CHECK_INTERFACE(IAudioFlingerClient, data, reply);
             int event = data.readInt32();
             int ioHandle = data.readInt32();
-            void *param2 = 0;
+            void *param2 = NULL;
             AudioSystem::OutputDescriptor desc;
             uint32_t stream;
             if (event == AudioSystem::STREAM_CONFIG_CHANGED) {
diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp
index 5ceb912..e6e989d 100644
--- a/media/libmedia/ToneGenerator.cpp
+++ b/media/libmedia/ToneGenerator.cpp
@@ -751,7 +751,7 @@
 
 // Used by ToneGenerator::getToneForRegion() to convert user specified supervisory tone type
 // to actual tone for current region.
-const unsigned char ToneGenerator::sToneMappingTable[NUM_REGIONS-1][NUM_SUP_TONES] = {
+const unsigned char /*tone_type*/ ToneGenerator::sToneMappingTable[NUM_REGIONS-1][NUM_SUP_TONES] = {
         {   // ANSI
             TONE_ANSI_DIAL,             // TONE_SUP_DIAL
             TONE_ANSI_BUSY,             // TONE_SUP_BUSY
@@ -811,9 +811,9 @@
     mThreadCanCallJava = threadCanCallJava;
     mStreamType = streamType;
     mVolume = volume;
-    mpAudioTrack = 0;
-    mpToneDesc = 0;
-    mpNewToneDesc = 0;
+    mpAudioTrack = NULL;
+    mpToneDesc = NULL;
+    mpNewToneDesc = NULL;
     // Generate tone by chunks of 20 ms to keep cadencing precision
     mProcessSize = (mSamplingRate * 20) / 1000;
 
@@ -855,7 +855,7 @@
 ToneGenerator::~ToneGenerator() {
     ALOGV("ToneGenerator destructor\n");
 
-    if (mpAudioTrack) {
+    if (mpAudioTrack != NULL) {
         stopTone();
         ALOGV("Delete Track: %p\n", mpAudioTrack);
         delete mpAudioTrack;
@@ -878,7 +878,7 @@
 //        none
 //
 ////////////////////////////////////////////////////////////////////////////////
-bool ToneGenerator::startTone(int toneType, int durationMs) {
+bool ToneGenerator::startTone(tone_type toneType, int durationMs) {
     bool lResult = false;
     status_t lStatus;
 
@@ -1012,7 +1012,7 @@
 
     if (mpAudioTrack) {
         delete mpAudioTrack;
-        mpAudioTrack = 0;
+        mpAudioTrack = NULL;
     }
 
    // Open audio track in mono, PCM 16bit, default sampling rate, default buffer size
@@ -1048,7 +1048,7 @@
     if (mpAudioTrack) {
         ALOGV("Delete Track I: %p\n", mpAudioTrack);
         delete mpAudioTrack;
-        mpAudioTrack = 0;
+        mpAudioTrack = NULL;
     }
 
     return false;
@@ -1317,7 +1317,7 @@
 bool ToneGenerator::prepareWave() {
     unsigned int segmentIdx = 0;
 
-    if (!mpNewToneDesc) {
+    if (mpNewToneDesc == NULL) {
         return false;
     }
 
@@ -1434,13 +1434,13 @@
 //        none
 //
 ////////////////////////////////////////////////////////////////////////////////
-int ToneGenerator::getToneForRegion(int toneType) {
-    int regionTone;
+ToneGenerator::tone_type ToneGenerator::getToneForRegion(tone_type toneType) {
+    tone_type regionTone;
 
     if (mRegion == CEPT || toneType < FIRST_SUP_TONE || toneType > LAST_SUP_TONE) {
         regionTone = toneType;
     } else {
-        regionTone = sToneMappingTable[mRegion][toneType - FIRST_SUP_TONE];
+        regionTone = (tone_type) sToneMappingTable[mRegion][toneType - FIRST_SUP_TONE];
     }
 
     ALOGV("getToneForRegion, tone %d, region %d, regionTone %d", toneType, mRegion, regionTone);
diff --git a/media/libstagefright/AACWriter.cpp b/media/libstagefright/AACWriter.cpp
index 1673ccd..9cdb463 100644
--- a/media/libstagefright/AACWriter.cpp
+++ b/media/libstagefright/AACWriter.cpp
@@ -60,7 +60,7 @@
 
 AACWriter::~AACWriter() {
     if (mStarted) {
-        stop();
+        reset();
     }
 
     if (mFd != -1) {
@@ -152,7 +152,7 @@
     return OK;
 }
 
-status_t AACWriter::stop() {
+status_t AACWriter::reset() {
     if (!mStarted) {
         return OK;
     }
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index 6c4e307..59b4ca7 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -52,7 +52,7 @@
 
 AMRWriter::~AMRWriter() {
     if (mStarted) {
-        stop();
+        reset();
     }
 
     if (mFd != -1) {
@@ -152,7 +152,7 @@
     return OK;
 }
 
-status_t AMRWriter::stop() {
+status_t AMRWriter::reset() {
     if (!mStarted) {
         return OK;
     }
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 03e8a06..483e5ab 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -78,6 +78,7 @@
 
 LOCAL_STATIC_LIBRARIES := \
         libstagefright_color_conversion \
+        libstagefright_aacenc \
         libstagefright_avcenc \
         libstagefright_m4vh263enc \
         libstagefright_matroska \
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 8bdb7c5..fef2a00 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -72,7 +72,7 @@
 
 AudioSource::~AudioSource() {
     if (mStarted) {
-        stop();
+        reset();
     }
 
     delete mRecord;
@@ -130,7 +130,7 @@
     }
 }
 
-status_t AudioSource::stop() {
+status_t AudioSource::reset() {
     Mutex::Autolock autoLock(mLock);
     if (!mStarted) {
         return UNKNOWN_ERROR;
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 1850c9c..228659c 100755
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -548,7 +548,7 @@
 
 CameraSource::~CameraSource() {
     if (mStarted) {
-        stop();
+        reset();
     } else if (mInitCheck == OK) {
         // Camera is initialized but because start() is never called,
         // the lock on Camera is never released(). This makes sure
@@ -632,8 +632,8 @@
     mCameraFlags = 0;
 }
 
-status_t CameraSource::stop() {
-    ALOGD("stop: E");
+status_t CameraSource::reset() {
+    ALOGD("reset: E");
     Mutex::Autolock autoLock(mLock);
     mStarted = false;
     mFrameAvailableCondition.signal();
@@ -670,7 +670,7 @@
     }
 
     CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped);
-    ALOGD("stop: X");
+    ALOGD("reset: X");
     return OK;
 }
 
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 263ab50..83d67b9 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -87,6 +87,10 @@
 }
 
 CameraSourceTimeLapse::~CameraSourceTimeLapse() {
+    if (mLastReadBufferCopy) {
+        mLastReadBufferCopy->release();
+        mLastReadBufferCopy = NULL;
+    }
 }
 
 void CameraSourceTimeLapse::startQuickReadReturns() {
@@ -204,15 +208,6 @@
     }
 }
 
-void CameraSourceTimeLapse::stopCameraRecording() {
-    ALOGV("stopCameraRecording");
-    CameraSource::stopCameraRecording();
-    if (mLastReadBufferCopy) {
-        mLastReadBufferCopy->release();
-        mLastReadBufferCopy = NULL;
-    }
-}
-
 sp<IMemory> CameraSourceTimeLapse::createIMemoryCopy(
         const sp<IMemory> &source_data) {
 
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index 36009ab..0b4ecbe 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -513,7 +513,7 @@
 
 MPEG2TSWriter::~MPEG2TSWriter() {
     if (mStarted) {
-        stop();
+        reset();
     }
 
     mLooper->unregisterHandler(mReflector->id());
@@ -564,7 +564,7 @@
     return OK;
 }
 
-status_t MPEG2TSWriter::stop() {
+status_t MPEG2TSWriter::reset() {
     CHECK(mStarted);
 
     for (size_t i = 0; i < mSources.size(); ++i) {
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 06dd875..068660b 100755
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -282,7 +282,7 @@
 }
 
 MPEG4Writer::~MPEG4Writer() {
-    stop();
+    reset();
 
     while (!mTracks.empty()) {
         List<Track *>::iterator it = mTracks.begin();
@@ -616,7 +616,7 @@
     mStarted = false;
 }
 
-status_t MPEG4Writer::stop() {
+status_t MPEG4Writer::reset() {
     if (mInitCheck != OK) {
         return OK;
     } else {
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index af4aa79..381320b 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "OMXCodec"
 #include <utils/Log.h>
 
+#include "include/AACEncoder.h"
 #include "include/AVCEncoder.h"
 #include "include/M4vH263Encoder.h"
 
@@ -68,6 +69,7 @@
 
 #define FACTORY_REF(name) { #name, Make##name },
 
+FACTORY_CREATE_ENCODER(AACEncoder)
 FACTORY_CREATE_ENCODER(AVCEncoder)
 FACTORY_CREATE_ENCODER(M4vH263Encoder)
 
@@ -80,6 +82,7 @@
     };
 
     static const FactoryInfo kFactoryInfo[] = {
+        FACTORY_REF(AACEncoder)
         FACTORY_REF(AVCEncoder)
         FACTORY_REF(M4vH263Encoder)
     };
@@ -145,6 +148,7 @@
     { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.google.amrwb.encoder" },
     { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.encode" },
     { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.google.aac.encoder" },
+    { MEDIA_MIMETYPE_AUDIO_AAC, "AACEncoder" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.DUCATI1.VIDEO.MPEG4E" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.encoder.mpeg4" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.encoder.mpeg4" },
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index 2233d1b..d068381 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -60,7 +60,7 @@
 SurfaceMediaSource::~SurfaceMediaSource() {
     ALOGV("SurfaceMediaSource::~SurfaceMediaSource");
     if (!mStopped) {
-        stop();
+        reset();
     }
 }
 
@@ -716,9 +716,9 @@
 }
 
 
-status_t SurfaceMediaSource::stop()
+status_t SurfaceMediaSource::reset()
 {
-    ALOGV("Stop");
+    ALOGV("Reset");
 
     Mutex::Autolock lock(mMutex);
     // TODO: Add waiting on mFrameCompletedCondition here?
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
index 0914f32..c79e01f 100644
--- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -333,8 +333,9 @@
 
 void SimpleSoftOMXComponent::onMessageReceived(const sp<AMessage> &msg) {
     Mutex::Autolock autoLock(mLock);
-
-    switch (msg->what()) {
+    uint32_t msgType = msg->what();
+    ALOGV("msgType = %d", msgType);
+    switch (msgType) {
         case kWhatSendCommand:
         {
             int32_t cmd, param;
@@ -354,27 +355,27 @@
             CHECK(mState == OMX_StateExecuting && mTargetState == mState);
 
             bool found = false;
-            for (size_t i = 0; i < mPorts.size(); ++i) {
-                PortInfo *port = &mPorts.editItemAt(i);
+            size_t portIndex = (kWhatEmptyThisBuffer == msgType)?
+                    header->nInputPortIndex: header->nOutputPortIndex;
+            PortInfo *port = &mPorts.editItemAt(portIndex);
 
-                for (size_t j = 0; j < port->mBuffers.size(); ++j) {
-                    BufferInfo *buffer = &port->mBuffers.editItemAt(j);
+            for (size_t j = 0; j < port->mBuffers.size(); ++j) {
+                BufferInfo *buffer = &port->mBuffers.editItemAt(j);
 
-                    if (buffer->mHeader == header) {
-                        CHECK(!buffer->mOwnedByUs);
+                if (buffer->mHeader == header) {
+                    CHECK(!buffer->mOwnedByUs);
 
-                        buffer->mOwnedByUs = true;
+                    buffer->mOwnedByUs = true;
 
-                        CHECK((msg->what() == kWhatEmptyThisBuffer
-                                    && port->mDef.eDir == OMX_DirInput)
-                                || (port->mDef.eDir == OMX_DirOutput));
+                    CHECK((msgType == kWhatEmptyThisBuffer
+                            && port->mDef.eDir == OMX_DirInput)
+                            || (port->mDef.eDir == OMX_DirOutput));
 
-                        port->mQueue.push_back(buffer);
-                        onQueueFilled(i);
+                    port->mQueue.push_back(buffer);
+                    onQueueFilled(portIndex);
 
-                        found = true;
-                        break;
-                    }
+                    found = true;
+                    break;
                 }
             }
 
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 5cf5236..6b2ae51 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -345,42 +345,73 @@
 void egl_display_t::loseCurrent(egl_context_t * cur_c)
 {
     if (cur_c) {
-        egl_surface_t * cur_r = get_surface(cur_c->read);
-        egl_surface_t * cur_d = get_surface(cur_c->draw);
+        egl_display_t* display = cur_c->getDisplay();
+        if (display) {
+            display->loseCurrentImpl(cur_c);
+        }
+    }
+}
 
-        // by construction, these are either 0 or valid (possibly terminated)
-        // it should be impossible for these to be invalid
-        ContextRef _cur_c(cur_c);
-        SurfaceRef _cur_r(cur_r);
-        SurfaceRef _cur_d(cur_d);
+void egl_display_t::loseCurrentImpl(egl_context_t * cur_c)
+{
+    // by construction, these are either 0 or valid (possibly terminated)
+    // it should be impossible for these to be invalid
+    ContextRef _cur_c(cur_c);
+    SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : NULL);
+    SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL);
 
+    { // scope for the lock
+        Mutex::Autolock _l(lock);
         cur_c->onLooseCurrent();
 
-        _cur_c.release();
-        _cur_r.release();
-        _cur_d.release();
     }
+
+    // This cannot be called with the lock held because it might end-up
+    // calling back into EGL (in particular when a surface is destroyed
+    // it calls ANativeWindow::disconnect
+    _cur_c.release();
+    _cur_r.release();
+    _cur_d.release();
 }
 
 EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c,
         EGLSurface draw, EGLSurface read, EGLContext ctx,
         EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx)
 {
-    Mutex::Autolock _l(lock);
     EGLBoolean result;
-    if (c) {
-        result = c->cnx->egl.eglMakeCurrent(
-                disp[c->impl].dpy, impl_draw, impl_read, impl_ctx);
-    } else {
-        result = cur_c->cnx->egl.eglMakeCurrent(
-                disp[cur_c->impl].dpy, impl_draw, impl_read, impl_ctx);
-    }
-    if (result == EGL_TRUE) {
-        loseCurrent(cur_c);
+
+    // by construction, these are either 0 or valid (possibly terminated)
+    // it should be impossible for these to be invalid
+    ContextRef _cur_c(cur_c);
+    SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : NULL);
+    SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL);
+
+    { // scope for the lock
+        Mutex::Autolock _l(lock);
         if (c) {
-            c->onMakeCurrent(draw, read);
+            result = c->cnx->egl.eglMakeCurrent(
+                    disp[c->impl].dpy, impl_draw, impl_read, impl_ctx);
+            if (result == EGL_TRUE) {
+                c->onMakeCurrent(draw, read);
+            }
+        } else {
+            result = cur_c->cnx->egl.eglMakeCurrent(
+                    disp[cur_c->impl].dpy, impl_draw, impl_read, impl_ctx);
+            if (result == EGL_TRUE) {
+                cur_c->onLooseCurrent();
+            }
         }
     }
+
+    if (result == EGL_TRUE) {
+        // This cannot be called with the lock held because it might end-up
+        // calling back into EGL (in particular when a surface is destroyed
+        // it calls ANativeWindow::disconnect
+        _cur_c.release();
+        _cur_r.release();
+        _cur_d.release();
+    }
+
     return result;
 }
 
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 4479e00..f3c4ddf 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -64,6 +64,7 @@
 class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes
     static egl_display_t sDisplay[NUM_DISPLAYS];
     EGLDisplay getDisplay(EGLNativeDisplayType display);
+    void loseCurrentImpl(egl_context_t * cur_c);
 
 public:
     enum {
diff --git a/packages/SystemUI/lint.xml b/packages/SystemUI/lint.xml
new file mode 100644
index 0000000..59a7109
--- /dev/null
+++ b/packages/SystemUI/lint.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lint>
+    <issue id="PrivateResource" severity="ignore" />
+</lint>
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/net/NetworkOverLimitActivity.java b/packages/SystemUI/src/com/android/systemui/net/NetworkOverLimitActivity.java
index 723e338..888b76e 100644
--- a/packages/SystemUI/src/com/android/systemui/net/NetworkOverLimitActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/net/NetworkOverLimitActivity.java
@@ -77,7 +77,7 @@
         final INetworkPolicyManager policyService = INetworkPolicyManager.Stub.asInterface(
                 ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
         try {
-            policyService.snoozePolicy(template);
+            policyService.snoozeLimit(template);
         } catch (RemoteException e) {
             Slog.w(TAG, "problem snoozing network policy", e);
         }
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 38c85bb..bcba3c2 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -16,16 +16,23 @@
 
 package com.android.internal.policy.impl;
 
-import android.app.Activity;
+import com.android.internal.app.ShutdownThread;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.R;
+
+import android.app.ActivityManagerNative;
 import android.app.AlertDialog;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.UserInfo;
 import android.media.AudioManager;
 import android.os.Handler;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.telephony.PhoneStateListener;
@@ -39,13 +46,9 @@
 import android.widget.BaseAdapter;
 import android.widget.ImageView;
 import android.widget.TextView;
-import com.android.internal.R;
-import com.android.internal.app.ShutdownThread;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
-import com.google.android.collect.Lists;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Helper to show the global actions dialog.  Each item is an {@link Action} that
@@ -101,9 +104,10 @@
     public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
         mKeyguardShowing = keyguardShowing;
         mDeviceProvisioned = isDeviceProvisioned;
-        if (mDialog == null) {
-            mDialog = createDialog();
+        if (mDialog != null) {
+            mDialog.dismiss();
         }
+        mDialog = createDialog();
         prepareDialog();
 
         mDialog.show();
@@ -187,6 +191,31 @@
             mItems.add(mSilentModeAction);
         }
 
+        List<UserInfo> users = mContext.getPackageManager().getUsers();
+        if (users.size() > 1) {
+            for (final UserInfo user : users) {
+                SinglePressAction switchToUser = new SinglePressAction(
+                        com.android.internal.R.drawable.ic_menu_cc,
+                        user.name != null ? user.name : "Primary") {
+                    public void onPress() {
+                        try {
+                            ActivityManagerNative.getDefault().switchUser(user.id);
+                        } catch (RemoteException re) {
+                            Log.e(TAG, "Couldn't switch user " + re);
+                        }
+                    }
+
+                    public boolean showDuringKeyguard() {
+                        return true;
+                    }
+
+                    public boolean showBeforeProvisioning() {
+                        return false;
+                    }
+                };
+                mItems.add(switchToUser);
+            }
+        }
         mAdapter = new MyAdapter();
 
         final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
@@ -341,12 +370,19 @@
     private static abstract class SinglePressAction implements Action {
         private final int mIconResId;
         private final int mMessageResId;
+        private final CharSequence mMessage;
 
         protected SinglePressAction(int iconResId, int messageResId) {
             mIconResId = iconResId;
             mMessageResId = messageResId;
+            mMessage = null;
         }
 
+        protected SinglePressAction(int iconResId, CharSequence message) {
+            mIconResId = iconResId;
+            mMessageResId = 0;
+            mMessage = message;
+        }
         public boolean isEnabled() {
             return true;
         }
@@ -363,7 +399,11 @@
             v.findViewById(R.id.status).setVisibility(View.GONE);
 
             icon.setImageDrawable(context.getResources().getDrawable(mIconResId));
-            messageView.setText(mMessageResId);
+            if (mMessage != null) {
+                messageView.setText(mMessage);
+            } else {
+                messageView.setText(mMessageResId);
+            }
 
             return v;
         }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 2d6e4f8..d34ed177 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -2389,8 +2389,9 @@
     }
 
     /** {@inheritDoc} */
-    public int finishLayoutLw() {
-        return 0;
+    @Override
+    public void finishLayoutLw() {
+        return;
     }
 
     /** {@inheritDoc} */
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 2d856ad..f71ba0a 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -160,7 +160,10 @@
 
 AudioFlinger::AudioFlinger()
     : BnAudioFlinger(),
-        mPrimaryHardwareDev(NULL), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1),
+        mPrimaryHardwareDev(NULL),
+        mHardwareStatus(AUDIO_HW_IDLE), // see also onFirstRef()
+        mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1),
+        mMode(AUDIO_MODE_INVALID),
         mBtNrecIsOff(false)
 {
 }
@@ -172,7 +175,6 @@
     Mutex::Autolock _l(mLock);
 
     /* TODO: move all this work into an Init() function */
-    mHardwareStatus = AUDIO_HW_IDLE;
 
     for (size_t i = 0; i < ARRAY_SIZE(audio_interfaces); i++) {
         const hw_module_t *mod;
@@ -265,13 +267,10 @@
 
     result.append("Clients:\n");
     for (size_t i = 0; i < mClients.size(); ++i) {
-        wp<Client> wClient = mClients.valueAt(i);
-        if (wClient != 0) {
-            sp<Client> client = wClient.promote();
-            if (client != 0) {
-                snprintf(buffer, SIZE, "  pid: %d\n", client->pid());
-                result.append(buffer);
-            }
+        sp<Client> client = mClients.valueAt(i).promote();
+        if (client != 0) {
+            snprintf(buffer, SIZE, "  pid: %d\n", client->pid());
+            result.append(buffer);
         }
     }
 
@@ -971,7 +970,8 @@
 {
     size_t size = mNotificationClients.size();
     for (size_t i = 0; i < size; i++) {
-        mNotificationClients.valueAt(i)->client()->ioConfigChanged(event, ioHandle, param2);
+        mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioHandle,
+                                                                               param2);
     }
 }
 
@@ -985,13 +985,19 @@
 
 // ----------------------------------------------------------------------------
 
-AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id, uint32_t device)
+AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id, uint32_t device,
+        type_t type)
     :   Thread(false),
-        mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mChannelCount(0),
-        mFrameSize(1), mFormat(AUDIO_FORMAT_INVALID), mStandby(false), mId(id), mExiting(false),
-        mDevice(device)
+        mType(type),
+        mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0),
+        // mChannelMask
+        mChannelCount(0),
+        mFrameSize(1), mFormat(AUDIO_FORMAT_INVALID),
+        mParamStatus(NO_ERROR),
+        mStandby(false), mId(id), mExiting(false),
+        mDevice(device),
+        mDeathRecipient(new PMDeathRecipient(this))
 {
-    mDeathRecipient = new PMDeathRecipient(this);
 }
 
 AudioFlinger::ThreadBase::~ThreadBase()
@@ -1372,20 +1378,24 @@
 AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger,
                                              AudioStreamOut* output,
                                              int id,
-                                             uint32_t device)
-    :   ThreadBase(audioFlinger, id, device),
-        mMixBuffer(NULL), mSuspended(0), mBytesWritten(0), mOutput(output),
+                                             uint32_t device,
+                                             type_t type)
+    :   ThreadBase(audioFlinger, id, device, type),
+        mMixBuffer(NULL), mSuspended(0), mBytesWritten(0),
+        // Assumes constructor is called by AudioFlinger with it's mLock held,
+        // but it would be safer to explicitly pass initial masterMute as parameter
+        mMasterMute(audioFlinger->masterMute_l()),
+        // mStreamTypes[] initialized in constructor body
+        mOutput(output),
+        // Assumes constructor is called by AudioFlinger with it's mLock held,
+        // but it would be safer to explicitly pass initial masterVolume as parameter
+        mMasterVolume(audioFlinger->masterVolume_l()),
         mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false)
 {
     snprintf(mName, kNameLength, "AudioOut_%d", id);
 
     readOutputParameters();
 
-    // Assumes constructor is called by AudioFlinger with it's mLock held,
-    // but it would be safer to explicitly pass these as parameters
-    mMasterVolume = mAudioFlinger->masterVolume_l();
-    mMasterMute = mAudioFlinger->masterMute_l();
-
     // mStreamTypes[AUDIO_STREAM_CNT] is initialized by stream_type_t default constructor
     // There is no AUDIO_STREAM_MIN, and ++ operator does not compile
     for (audio_stream_type_t stream = (audio_stream_type_t) 0; stream < AUDIO_STREAM_CNT;
@@ -1431,13 +1441,10 @@
     result.append(buffer);
     result.append("   Name  Clien Typ Fmt Chn mask   Session Buf  S M F SRate LeftV RighV  Serv       User       Main buf   Aux Buf\n");
     for (size_t i = 0; i < mActiveTracks.size(); ++i) {
-        wp<Track> wTrack = mActiveTracks[i];
-        if (wTrack != 0) {
-            sp<Track> track = wTrack.promote();
-            if (track != 0) {
-                track->dump(buffer, SIZE);
-                result.append(buffer);
-            }
+        sp<Track> track = mActiveTracks[i].promote();
+        if (track != 0) {
+            track->dump(buffer, SIZE);
+            result.append(buffer);
         }
     }
     write(fd, result.string(), result.size());
@@ -1705,7 +1712,7 @@
 // audioConfigChanged_l() must be called with AudioFlinger::mLock held
 void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) {
     AudioSystem::OutputDescriptor desc;
-    void *param2 = 0;
+    void *param2 = NULL;
 
     ALOGV("PlaybackThread::audioConfigChanged_l, thread %p, event %d, param %d", this, event, param);
 
@@ -1740,7 +1747,7 @@
 
     // FIXME - Current mixer implementation only supports stereo output: Always
     // Allocate a stereo buffer even if HW output is mono.
-    if (mMixBuffer != NULL) delete[] mMixBuffer;
+    delete[] mMixBuffer;
     mMixBuffer = new int16_t[mFrameCount * 2];
     memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t));
 
@@ -1758,7 +1765,7 @@
 
 status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames)
 {
-    if (halFrames == 0 || dspFrames == 0) {
+    if (halFrames == NULL || dspFrames == NULL) {
         return BAD_VALUE;
     }
     Mutex::Autolock _l(mLock);
@@ -1845,13 +1852,12 @@
 
 // ----------------------------------------------------------------------------
 
-AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
-    :   PlaybackThread(audioFlinger, output, id, device),
-        mAudioMixer(NULL), mPrevMixerStatus(MIXER_IDLE)
+AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
+        int id, uint32_t device, type_t type)
+    :   PlaybackThread(audioFlinger, output, id, device, type),
+        mAudioMixer(new AudioMixer(mFrameCount, mSampleRate)),
+        mPrevMixerStatus(MIXER_IDLE)
 {
-    mType = ThreadBase::MIXER;
-    mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
-
     // FIXME - Current mixer implementation only supports stereo output
     if (mChannelCount == 1) {
         ALOGE("Invalid audio hardware channel count");
@@ -2193,7 +2199,7 @@
                 // read original volumes with volume control
                 float typeVolume = mStreamTypes[track->type()].volume;
                 float v = masterVolume * typeVolume;
-                uint32_t vlr = cblk->volumeLR;
+                uint32_t vlr = cblk->getVolumeLR();
                 vl = vlr & 0xFFFF;
                 vr = vlr >> 16;
                 // track volumes come from shared memory, so can't be trusted and must be clamped
@@ -2461,6 +2467,8 @@
             }
             if (status == NO_ERROR && reconfig) {
                 delete mAudioMixer;
+                // for safety in case readOutputParameters() accesses mAudioMixer (it doesn't)
+                mAudioMixer = NULL;
                 readOutputParameters();
                 mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
                 for (size_t i = 0; i < mTracks.size() ; i++) {
@@ -2513,9 +2521,10 @@
 
 // ----------------------------------------------------------------------------
 AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
-    :   PlaybackThread(audioFlinger, output, id, device)
+    :   PlaybackThread(audioFlinger, output, id, device, DIRECT)
+        // mLeftVolFloat, mRightVolFloat
+        // mLeftVolShort, mRightVolShort
 {
-    mType = ThreadBase::DIRECT;
 }
 
 AudioFlinger::DirectOutputThread::~DirectOutputThread()
@@ -2729,7 +2738,7 @@
                     } else {
                         float typeVolume = mStreamTypes[track->type()].volume;
                         float v = mMasterVolume * typeVolume;
-                        uint32_t vlr = cblk->volumeLR;
+                        uint32_t vlr = cblk->getVolumeLR();
                         float v_clamped = v * (vlr & 0xFFFF);
                         if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
                         left = v_clamped/MAX_GAIN;
@@ -2992,10 +3001,11 @@
 
 // ----------------------------------------------------------------------------
 
-AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id)
-    :   MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->device()), mWaitTimeMs(UINT_MAX)
+AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger,
+        AudioFlinger::MixerThread* mainThread, int id)
+    :   MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->device(), DUPLICATING),
+        mWaitTimeMs(UINT_MAX)
 {
-    mType = ThreadBase::DUPLICATING;
     addOutputTrack(mainThread);
 }
 
@@ -3244,13 +3254,17 @@
     :   RefBase(),
         mThread(thread),
         mClient(client),
-        mCblk(0),
+        mCblk(NULL),
+        // mBuffer
+        // mBufferEnd
         mFrameCount(0),
         mState(IDLE),
         mClientTid(-1),
         mFormat(format),
         mFlags(flags & ~SYSTEM_FLAGS_MASK),
         mSessionId(sessionId)
+        // mChannelCount
+        // mChannelMask
 {
     ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size());
 
@@ -3266,7 +3280,7 @@
         mCblkMemory = client->heap()->allocate(size);
         if (mCblkMemory != 0) {
             mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer());
-            if (mCblk) { // construct the shared structure in-place.
+            if (mCblk != NULL) { // construct the shared structure in-place.
                 new(mCblk) audio_track_cblk_t();
                 // clear all buffers
                 mCblk->frameCount = frameCount;
@@ -3309,7 +3323,7 @@
 
 AudioFlinger::ThreadBase::TrackBase::~TrackBase()
 {
-    if (mCblk) {
+    if (mCblk != NULL) {
         mCblk->~audio_track_cblk_t();   // destroy our shared-structure.
         if (mClient == NULL) {
             delete mCblk;
@@ -3317,6 +3331,7 @@
     }
     mCblkMemory.clear();            // and free the shared memory
     if (mClient != NULL) {
+        // Client destructor must run with AudioFlinger mutex locked
         Mutex::Autolock _l(mClient->audioFlinger()->mLock);
         mClient.clear();
     }
@@ -3383,7 +3398,7 @@
                 server %d, serverBase %d, user %d, userBase %d",
                 bufferStart, bufferEnd, mBuffer, mBufferEnd,
                 cblk->server, cblk->serverBase, cblk->user, cblk->userBase);
-        return 0;
+        return NULL;
     }
 
     return bufferStart;
@@ -3468,7 +3483,7 @@
 
 void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
 {
-    uint32_t vlr = mCblk->volumeLR;
+    uint32_t vlr = mCblk->getVolumeLR();
     snprintf(buffer, size, "   %05d %05d %03u %03u 0x%08x %05u   %04u %1d %1d %1d %05u %05u %05u  0x%08x 0x%08x 0x%08x 0x%08x\n",
             mName - AudioMixer::TRACK0,
             (mClient == NULL) ? getpid() : mClient->pid(),
@@ -3827,7 +3842,6 @@
     if (mCblk != NULL) {
         mCblk->flags |= CBLK_DIRECTION_OUT;
         mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
-        mCblk->volumeLR = (MAX_GAIN_INT << 16) | MAX_GAIN_INT;
         mOutBuffer.frameCount = 0;
         playbackThread->mTracks.add(this);
         ALOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, " \
@@ -4065,7 +4079,7 @@
     mAudioFlinger->removeClient_l(mPid);
 }
 
-const sp<MemoryDealer>& AudioFlinger::Client::heap() const
+sp<MemoryDealer> AudioFlinger::Client::heap() const
 {
     return mMemoryDealer;
 }
@@ -4075,13 +4089,12 @@
 AudioFlinger::NotificationClient::NotificationClient(const sp<AudioFlinger>& audioFlinger,
                                                      const sp<IAudioFlingerClient>& client,
                                                      pid_t pid)
-    : mAudioFlinger(audioFlinger), mPid(pid), mClient(client)
+    : mAudioFlinger(audioFlinger), mPid(pid), mAudioFlingerClient(client)
 {
 }
 
 AudioFlinger::NotificationClient::~NotificationClient()
 {
-    mClient.clear();
 }
 
 void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who)
@@ -4266,15 +4279,16 @@
                                          uint32_t channels,
                                          int id,
                                          uint32_t device) :
-    ThreadBase(audioFlinger, id, device),
-    mInput(input), mTrack(NULL), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL)
+    ThreadBase(audioFlinger, id, device, RECORD),
+    mInput(input), mTrack(NULL), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
+    // mRsmpInIndex and mInputBytes set by readInputParameters()
+    mReqChannelCount(popcount(channels)),
+    mReqSampleRate(sampleRate)
+    // mBytesRead is only meaningful while active, and so is cleared in start()
+    // (but might be better to also clear here for dump?)
 {
-    mType = ThreadBase::RECORD;
-
     snprintf(mName, kNameLength, "AudioIn_%d", id);
 
-    mReqChannelCount = popcount(channels);
-    mReqSampleRate = sampleRate;
     readInputParameters();
 }
 
@@ -4282,10 +4296,8 @@
 AudioFlinger::RecordThread::~RecordThread()
 {
     delete[] mRsmpInBuffer;
-    if (mResampler != NULL) {
-        delete mResampler;
-        delete[] mRsmpOutBuffer;
-    }
+    delete mResampler;
+    delete[] mRsmpOutBuffer;
 }
 
 void AudioFlinger::RecordThread::onFirstRef()
@@ -4807,7 +4819,7 @@
 
 void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) {
     AudioSystem::OutputDescriptor desc;
-    void *param2 = 0;
+    void *param2 = NULL;
 
     switch (event) {
     case AudioSystem::INPUT_OPENED:
@@ -4829,9 +4841,11 @@
 
 void AudioFlinger::RecordThread::readInputParameters()
 {
-    if (mRsmpInBuffer) delete mRsmpInBuffer;
-    if (mRsmpOutBuffer) delete mRsmpOutBuffer;
-    if (mResampler) delete mResampler;
+    delete mRsmpInBuffer;
+    // mRsmpInBuffer is always assigned a new[] below
+    delete mRsmpOutBuffer;
+    mRsmpOutBuffer = NULL;
+    delete mResampler;
     mResampler = NULL;
 
     mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common);
@@ -4983,10 +4997,10 @@
         }
         mPlaybackThreads.add(id, thread);
 
-        if (pSamplingRate) *pSamplingRate = samplingRate;
-        if (pFormat) *pFormat = format;
-        if (pChannels) *pChannels = channels;
-        if (pLatencyMs) *pLatencyMs = thread->latency();
+        if (pSamplingRate != NULL) *pSamplingRate = samplingRate;
+        if (pFormat != NULL) *pFormat = format;
+        if (pChannels != NULL) *pChannels = channels;
+        if (pLatencyMs != NULL) *pLatencyMs = thread->latency();
 
         // notify client processes of the new output creation
         thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);
@@ -5038,7 +5052,7 @@
                 }
             }
         }
-        void *param2 = 0;
+        void *param2 = NULL;
         audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, param2);
         mPlaybackThreads.removeItem(output);
     }
@@ -5089,7 +5103,7 @@
                                 uint32_t *pSamplingRate,
                                 audio_format_t *pFormat,
                                 uint32_t *pChannels,
-                                uint32_t acoustics)
+                                audio_in_acoustics_t acoustics)
 {
     status_t status;
     RecordThread *thread = NULL;
@@ -5114,7 +5128,7 @@
 
     status = inHwDev->open_input_stream(inHwDev, *pDevices, &format,
                                         &channels, &samplingRate,
-                                        (audio_in_acoustics_t)acoustics,
+                                        acoustics,
                                         &inStream);
     ALOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, acoustics %x, status %d",
             inStream,
@@ -5134,7 +5148,7 @@
         ALOGV("openInput() reopening with proposed sampling rate and channels");
         status = inHwDev->open_input_stream(inHwDev, *pDevices, &format,
                                             &channels, &samplingRate,
-                                            (audio_in_acoustics_t)acoustics,
+                                            acoustics,
                                             &inStream);
     }
 
@@ -5154,9 +5168,9 @@
                                   device);
         mRecordThreads.add(id, thread);
         ALOGV("openInput() created record thread: ID %d thread %p", id, thread);
-        if (pSamplingRate) *pSamplingRate = reqSamplingRate;
-        if (pFormat) *pFormat = format;
-        if (pChannels) *pChannels = reqChannels;
+        if (pSamplingRate != NULL) *pSamplingRate = reqSamplingRate;
+        if (pFormat != NULL) *pFormat = format;
+        if (pChannels != NULL) *pChannels = reqChannels;
 
         input->stream->common.standby(&input->stream->common);
 
@@ -5181,7 +5195,7 @@
         }
 
         ALOGV("closeInput() %d", input);
-        void *param2 = 0;
+        void *param2 = NULL;
         audioConfigChanged_l(AudioSystem::INPUT_CLOSED, input, param2);
         mRecordThreads.removeItem(input);
     }
@@ -5243,12 +5257,8 @@
             return;
         }
     }
-    AudioSessionRef *ref = new AudioSessionRef();
-    ref->sessionid = audioSession;
-    ref->pid = caller;
-    ref->cnt = 1;
-    mAudioSessionRefs.push(ref);
-    ALOGV(" added new entry for %d", ref->sessionid);
+    mAudioSessionRefs.push(new AudioSessionRef(audioSession, caller));
+    ALOGV(" added new entry for %d", audioSession);
 }
 
 void AudioFlinger::releaseAudioSessionId(int audioSession)
@@ -5792,7 +5802,7 @@
         // create effect handle and connect it to effect module
         handle = new EffectHandle(effect, client, effectClient, priority);
         lStatus = effect->addHandle(handle);
-        if (enabled) {
+        if (enabled != NULL) {
             *enabled = (int)effect->isEnabled();
         }
     }
@@ -6179,7 +6189,7 @@
     }
 }
 
-status_t AudioFlinger::EffectModule::addHandle(sp<EffectHandle>& handle)
+status_t AudioFlinger::EffectModule::addHandle(const sp<EffectHandle>& handle)
 {
     status_t status;
 
@@ -6226,7 +6236,7 @@
 
     bool enabled = false;
     EffectHandle *hdl = handle.unsafe_get();
-    if (hdl) {
+    if (hdl != NULL) {
         ALOGV("removeHandle() unsafe_get OK");
         enabled = hdl->enabled();
     }
@@ -6862,7 +6872,7 @@
     if (mCblkMemory != 0) {
         mCblk = static_cast<effect_param_cblk_t *>(mCblkMemory->pointer());
 
-        if (mCblk) {
+        if (mCblk != NULL) {
             new(mCblk) effect_param_cblk_t();
             mBuffer = (uint8_t *)mCblk + bufOffset;
          }
@@ -6959,7 +6969,7 @@
     // release sp on module => module destructor can be called now
     mEffect.clear();
     if (mClient != 0) {
-        if (mCblk) {
+        if (mCblk != NULL) {
             mCblk->~effect_param_cblk_t();   // destroy our shared-structure.
         }
         mCblkMemory.clear();            // and free the shared memory
@@ -7089,7 +7099,7 @@
 
 void AudioFlinger::EffectHandle::dump(char* buffer, size_t size)
 {
-    bool locked = mCblk ? tryLock(mCblk->lock) : false;
+    bool locked = mCblk != NULL && tryLock(mCblk->lock);
 
     snprintf(buffer, size, "\t\t\t%05d %05d    %01u    %01u      %05u  %05u\n",
             (mClient == NULL) ? getpid() : mClient->pid(),
@@ -7551,7 +7561,8 @@
             ALOGV("setEffectSuspendedAll_l() add entry for 0");
         }
         if (desc->mRefCount++ == 0) {
-            Vector< sp<EffectModule> > effects = getSuspendEligibleEffects();
+            Vector< sp<EffectModule> > effects;
+            getSuspendEligibleEffects(effects);
             for (size_t i = 0; i < effects.size(); i++) {
                 setEffectSuspended_l(&effects[i]->desc().type, true);
             }
@@ -7602,16 +7613,14 @@
     return true;
 }
 
-Vector< sp<AudioFlinger::EffectModule> > AudioFlinger::EffectChain::getSuspendEligibleEffects()
+void AudioFlinger::EffectChain::getSuspendEligibleEffects(Vector< sp<AudioFlinger::EffectModule> > &effects)
 {
-    Vector< sp<EffectModule> > effects;
+    effects.clear();
     for (size_t i = 0; i < mEffects.size(); i++) {
-        if (!isEffectEligibleForSuspend(mEffects[i]->desc())) {
-            continue;
+        if (isEffectEligibleForSuspend(mEffects[i]->desc())) {
+            effects.add(mEffects[i]);
         }
-        effects.add(mEffects[i]);
     }
-    return effects;
 }
 
 sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectIfEnabled(
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 766ba44..3f3188c 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -131,7 +131,7 @@
                             uint32_t *pSamplingRate,
                             audio_format_t *pFormat,
                             uint32_t *pChannels,
-                            uint32_t acoustics);
+                            audio_in_acoustics_t acoustics);
 
     virtual status_t closeInput(int input);
 
@@ -226,16 +226,16 @@
     public:
                             Client(const sp<AudioFlinger>& audioFlinger, pid_t pid);
         virtual             ~Client();
-        const sp<MemoryDealer>&     heap() const;
+        sp<MemoryDealer>    heap() const;
         pid_t               pid() const { return mPid; }
         sp<AudioFlinger>    audioFlinger() { return mAudioFlinger; }
 
     private:
                             Client(const Client&);
                             Client& operator = (const Client&);
-        sp<AudioFlinger>    mAudioFlinger;
-        sp<MemoryDealer>    mMemoryDealer;
-        pid_t               mPid;
+        const sp<AudioFlinger> mAudioFlinger;
+        const sp<MemoryDealer> mMemoryDealer;
+        const pid_t         mPid;
     };
 
     // --- Notification Client ---
@@ -246,7 +246,7 @@
                                                 pid_t pid);
         virtual             ~NotificationClient();
 
-                sp<IAudioFlingerClient>    client() { return mClient; }
+                sp<IAudioFlingerClient> audioFlingerClient() const { return mAudioFlingerClient; }
 
                 // IBinder::DeathRecipient
                 virtual     void        binderDied(const wp<IBinder>& who);
@@ -255,9 +255,9 @@
                             NotificationClient(const NotificationClient&);
                             NotificationClient& operator = (const NotificationClient&);
 
-        sp<AudioFlinger>        mAudioFlinger;
-        pid_t                   mPid;
-        sp<IAudioFlingerClient> mClient;
+        const sp<AudioFlinger>  mAudioFlinger;
+        const pid_t             mPid;
+        const sp<IAudioFlingerClient> mAudioFlingerClient;
     };
 
     class TrackHandle;
@@ -277,17 +277,17 @@
 
     class ThreadBase : public Thread {
     public:
-        ThreadBase (const sp<AudioFlinger>& audioFlinger, int id, uint32_t device);
-        virtual             ~ThreadBase();
 
-
-        enum type {
+        enum type_t {
             MIXER,              // Thread class is MixerThread
             DIRECT,             // Thread class is DirectOutputThread
             DUPLICATING,        // Thread class is DuplicatingThread
             RECORD              // Thread class is RecordThread
         };
 
+        ThreadBase (const sp<AudioFlinger>& audioFlinger, int id, uint32_t device, type_t type);
+        virtual             ~ThreadBase();
+
         status_t dumpBase(int fd, const Vector<String16>& args);
         status_t dumpEffectChains(int fd, const Vector<String16>& args);
 
@@ -367,8 +367,8 @@
             bool step();
             void reset();
 
-            wp<ThreadBase>      mThread;
-            sp<Client>          mClient;
+            const wp<ThreadBase> mThread;
+            /*const*/ sp<Client> mClient;   // see explanation at ~TrackBase() why not const
             sp<IMemory>         mCblkMemory;
             audio_track_cblk_t* mCblk;
             void*               mBuffer;
@@ -377,9 +377,9 @@
             // we don't really need a lock for these
             track_state         mState;
             int                 mClientTid;
-            audio_format_t      mFormat;
+            const audio_format_t mFormat;
             uint32_t            mFlags;
-            int                 mSessionId;
+            const int           mSessionId;
             uint8_t             mChannelCount;
             uint32_t            mChannelMask;
         };
@@ -408,7 +408,7 @@
         };
 
         virtual     status_t    initCheck() const = 0;
-                    int         type() const { return mType; }
+                    type_t      type() const { return mType; }
                     uint32_t    sampleRate() const;
                     int         channelCount() const;
                     audio_format_t format() const;
@@ -530,9 +530,9 @@
         friend class RecordThread;
         friend class RecordTrack;
 
-                    int                     mType;
+                    const type_t            mType;
                     Condition               mWaitWorkCV;
-                    sp<AudioFlinger>        mAudioFlinger;
+                    const sp<AudioFlinger>  mAudioFlinger;
                     uint32_t                mSampleRate;
                     size_t                  mFrameCount;
                     uint32_t                mChannelMask;
@@ -553,7 +553,7 @@
                     char                    mName[kNameLength];
                     sp<IPowerManager>       mPowerManager;
                     sp<IBinder>             mWakeLockToken;
-                    sp<PMDeathRecipient>    mDeathRecipient;
+                    const sp<PMDeathRecipient> mDeathRecipient;
                     // list of suspended effects per session and per type. The first vector is
                     // keyed by session ID, the second by type UUID timeLow field
                     KeyedVector< int, KeyedVector< int, sp<SuspendedSessionDesc> > >  mSuspendedSessions;
@@ -671,7 +671,7 @@
                     bool        write(int16_t* data, uint32_t frames);
                     bool        bufferQueueEmpty() { return (mBufferQueue.size() == 0) ? true : false; }
                     bool        isActive() { return mActive; }
-            wp<ThreadBase>&     thread()  { return mThread; }
+            const wp<ThreadBase>& thread() { return mThread; }
 
         private:
 
@@ -688,10 +688,11 @@
             Vector < Buffer* >          mBufferQueue;
             AudioBufferProvider::Buffer mOutBuffer;
             bool                        mActive;
-            DuplicatingThread*          mSourceThread;
+            DuplicatingThread* const mSourceThread; // for waitTimeMs() in write()
         };  // end of OutputTrack
 
-        PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device);
+        PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id,
+                        uint32_t device, type_t type);
         virtual             ~PlaybackThread();
 
         virtual     status_t    dump(int fd, const Vector<String16>& args);
@@ -817,7 +818,8 @@
         MixerThread (const sp<AudioFlinger>& audioFlinger,
                      AudioStreamOut* output,
                      int id,
-                     uint32_t device);
+                     uint32_t device,
+                     type_t type = MIXER);
         virtual             ~MixerThread();
 
         // Thread virtuals
@@ -917,7 +919,7 @@
         virtual status_t onTransact(
             uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
     private:
-        sp<PlaybackThread::Track> mTrack;
+        const sp<PlaybackThread::Track> mTrack;
     };
 
     friend class Client;
@@ -1021,8 +1023,8 @@
                 int16_t                             *mRsmpInBuffer;
                 size_t                              mRsmpInIndex;
                 size_t                              mInputBytes;
-                int                                 mReqChannelCount;
-                uint32_t                            mReqSampleRate;
+                const int                           mReqChannelCount;
+                const uint32_t                      mReqSampleRate;
                 ssize_t                             mBytesRead;
     };
 
@@ -1036,7 +1038,7 @@
         virtual status_t onTransact(
             uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
     private:
-        sp<RecordThread::RecordTrack> mRecordTrack;
+        const sp<RecordThread::RecordTrack> mRecordTrack;
     };
 
     //--- Audio Effect Management
@@ -1105,9 +1107,9 @@
         int16_t     *outBuffer() { return mConfig.outputCfg.buffer.s16; }
         void        setChain(const wp<EffectChain>& chain) { mChain = chain; }
         void        setThread(const wp<ThreadBase>& thread) { mThread = thread; }
-        wp<ThreadBase>& thread() { return mThread; }
+        const wp<ThreadBase>& thread() { return mThread; }
 
-        status_t addHandle(sp<EffectHandle>& handle);
+        status_t addHandle(const sp<EffectHandle>& handle);
         void disconnect(const wp<EffectHandle>& handle, bool unpiniflast);
         size_t removeHandle (const wp<EffectHandle>& handle);
 
@@ -1325,7 +1327,8 @@
 
         // get a list of effect modules to suspend when an effect of the type
         // passed is enabled.
-        Vector< sp<EffectModule> > getSuspendEligibleEffects();
+        void                       getSuspendEligibleEffects(Vector< sp<EffectModule> > &effects);
+
         // get an effect module if it is currently enable
         sp<EffectModule> getEffectIfEnabled(const effect_uuid_t *type);
         // true if the effect whose descriptor is passed can be suspended
@@ -1377,8 +1380,11 @@
     };
 
     struct AudioSessionRef {
-        int sessionid;
-        pid_t pid;
+        // FIXME rename parameter names when fields get "m" prefix
+        AudioSessionRef(int sessionid_, pid_t pid_) :
+            sessionid(sessionid_), pid(pid_), cnt(1) {}
+        const int sessionid;
+        const pid_t pid;
         int cnt;
     };
 
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index a8102e5..0b9f8ba 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -48,9 +48,10 @@
     mState.enabledTracks= 0;
     mState.needsChanged = 0;
     mState.frameCount   = frameCount;
+    mState.hook         = process__nop;
     mState.outputTemp   = NULL;
     mState.resampleTemp = NULL;
-    mState.hook         = process__nop;
+    // mState.reserved
     track_t* t = mState.tracks;
     for (unsigned i=0 ; i < MAX_NUM_TRACKS ; i++) {
         t->needs = 0;
@@ -70,12 +71,13 @@
         t->enabled = 0;
         t->format = 16;
         t->channelMask = AUDIO_CHANNEL_OUT_STEREO;
-        t->buffer.raw = 0;
         t->bufferProvider = NULL;
+        t->buffer.raw = NULL;
+        // t->buffer.frameCount
         t->hook = NULL;
+        t->in = NULL;
         t->resampler = NULL;
         t->sampleRate = mSampleRate;
-        t->in = NULL;
         t->mainBuffer = NULL;
         t->auxBuffer = NULL;
         t++;
@@ -123,7 +125,7 @@
         track.enabled = 0;
         invalidateState(1<<name);
     }
-    if (track.resampler) {
+    if (track.resampler != NULL) {
         // delete  the resampler
         delete track.resampler;
         track.resampler = NULL;
@@ -807,7 +809,7 @@
             while (outFrames) {
                 t1.buffer.frameCount = outFrames;
                 t1.bufferProvider->getNextBuffer(&t1.buffer);
-                if (!t1.buffer.raw) break;
+                if (t1.buffer.raw == NULL) break;
                 outFrames -= t1.buffer.frameCount;
                 t1.bufferProvider->releaseBuffer(&t1.buffer);
             }
@@ -1127,9 +1129,7 @@
         }
     }
 
-    if (buff != NULL) {
-        delete [] buff;
-    }
+    delete [] buff;
 }
 #endif
 
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index 2df1385..1dddbb3 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -144,9 +144,9 @@
     }
     mInputs.clear();
 
-    if (mpAudioPolicy && mpAudioPolicyDev)
+    if (mpAudioPolicy != NULL && mpAudioPolicyDev != NULL)
         mpAudioPolicyDev->destroy_audio_policy(mpAudioPolicyDev, mpAudioPolicy);
-    if (mpAudioPolicyDev)
+    if (mpAudioPolicyDev != NULL)
         audio_policy_dev_close(mpAudioPolicyDev);
 }
 
@@ -649,7 +649,7 @@
         release_wake_lock(mName.string());
     }
     mAudioCommands.clear();
-    if (mpToneGenerator != NULL) delete mpToneGenerator;
+    delete mpToneGenerator;
 }
 
 void AudioPolicyService::AudioCommandThread::onFirstRef()
@@ -682,8 +682,7 @@
                     ToneData *data = (ToneData *)command->mParam;
                     ALOGV("AudioCommandThread() processing start tone %d on stream %d",
                             data->mType, data->mStream);
-                    if (mpToneGenerator != NULL)
-                        delete mpToneGenerator;
+                    delete mpToneGenerator;
                     mpToneGenerator = new ToneGenerator(data->mStream, 1.0);
                     mpToneGenerator->startTone(data->mType);
                     delete data;
@@ -790,7 +789,8 @@
     return NO_ERROR;
 }
 
-void AudioPolicyService::AudioCommandThread::startToneCommand(int type, audio_stream_type_t stream)
+void AudioPolicyService::AudioCommandThread::startToneCommand(ToneGenerator::tone_type type,
+        audio_stream_type_t stream)
 {
     AudioCommand *command = new AudioCommand();
     command->mCommand = START_TONE;
@@ -1160,7 +1160,7 @@
     if (param == NULL && value == NULL) {
         // try to parse simple parameter form {int int}
         param = root->first_child;
-        if (param) {
+        if (param != NULL) {
             // Note: that a pair of random strings is read as 0 0
             int *ptr = (int *)fx_param->data;
             int *ptr2 = (int *)((char *)param + sizeof(effect_param_t));
@@ -1419,7 +1419,7 @@
                                             uint32_t *pSamplingRate,
                                             audio_format_t *pFormat,
                                             uint32_t *pChannels,
-                                            uint32_t acoustics)
+                                            audio_in_acoustics_t acoustics)
 {
     sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
     if (af == NULL) {
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
index 3c0f5ed..62219e5 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -79,7 +79,7 @@
                                     audio_format_t format = AUDIO_FORMAT_DEFAULT,
                                     uint32_t channels = 0,
                                     audio_in_acoustics_t acoustics =
-                                            (audio_in_acoustics_t)0,
+                                            (audio_in_acoustics_t)0 /*AUDIO_IN_ACOUSTICS_NONE*/,
                                     int audioSession = 0);
     virtual status_t startInput(audio_io_handle_t input);
     virtual status_t stopInput(audio_io_handle_t input);
@@ -171,7 +171,8 @@
         virtual     bool        threadLoop();
 
                     void        exit();
-                    void        startToneCommand(int type = 0, audio_stream_type_t stream = AUDIO_STREAM_VOICE_CALL);
+                    void        startToneCommand(ToneGenerator::tone_type type,
+                                                 audio_stream_type_t stream);
                     void        stopToneCommand();
                     status_t    volumeCommand(audio_stream_type_t stream, float volume, int output, int delayMs = 0);
                     status_t    parametersCommand(int ioHandle, const char *keyValuePairs, int delayMs = 0);
@@ -198,7 +199,7 @@
 
         class ToneData {
         public:
-            int mType;      // tone type (START_TONE only)
+            ToneGenerator::tone_type mType; // tone type (START_TONE only)
             audio_stream_type_t mStream;    // stream type (START_TONE only)
         };
 
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 4f81178..081f1f4 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -24,67 +24,35 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.Intent.FilterComparison;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.net.Uri;
 import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.SystemClock;
-import android.util.AttributeSet;
-import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
-import android.util.TypedValue;
-import android.util.Xml;
+import android.util.SparseArray;
 import android.widget.RemoteViews;
 
 import com.android.internal.appwidget.IAppWidgetHost;
 import com.android.internal.appwidget.IAppWidgetService;
-import com.android.internal.os.AtomicFile;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.widget.IRemoteViewsAdapterConnection;
-import com.android.internal.widget.IRemoteViewsFactory;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
-import java.util.Set;
 
+
+/**
+ * Redirects calls to this service to the instance of the service for the appropriate user.
+ */
 class AppWidgetService extends IAppWidgetService.Stub
 {
     private static final String TAG = "AppWidgetService";
 
-    private static final String SETTINGS_FILENAME = "appwidgets.xml";
-    private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
-
     /*
      * When identifying a Host or Provider based on the calling process, use the uid field.
      * When identifying a Host or Provider based on a package manager broadcast, use the
@@ -125,11 +93,9 @@
      * globally and may lead us to leak AppWidgetService instances (if there were more than one).
      */
     static class ServiceConnectionProxy implements ServiceConnection {
-        private final Pair<Integer, Intent.FilterComparison> mKey;
         private final IBinder mConnectionCb;
 
         ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
-            mKey = key;
             mConnectionCb = connectionCb;
         }
         public void onServiceConnected(ComponentName name, IBinder service) {
@@ -155,13 +121,6 @@
         }
     }
 
-    // Manages active connections to RemoteViewsServices
-    private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
-        mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>();
-    // Manages persistent references to RemoteViewsServices from different App Widgets
-    private final HashMap<FilterComparison, HashSet<Integer>>
-        mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
-
     Context mContext;
     Locale mLocale;
     PackageManager mPackageManager;
@@ -171,35 +130,32 @@
     final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
     ArrayList<Host> mHosts = new ArrayList<Host>();
     boolean mSafeMode;
-    boolean mStateLoaded;
 
-    // These are for debugging only -- widgets are going missing in some rare instances
-    ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
-    ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
+
+    private final SparseArray<AppWidgetServiceImpl> mAppWidgetServices;
 
     AppWidgetService(Context context) {
         mContext = context;
-        mPackageManager = context.getPackageManager();
-        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+        mAppWidgetServices = new SparseArray<AppWidgetServiceImpl>(5);
+        AppWidgetServiceImpl primary = new AppWidgetServiceImpl(context, 0);
+        mAppWidgetServices.append(0, primary);
     }
 
     public void systemReady(boolean safeMode) {
         mSafeMode = safeMode;
 
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-        }
+        mAppWidgetServices.get(0).systemReady(safeMode);
 
         // Register for the boot completed broadcast, so we can send the
-        // ENABLE broacasts.  If we try to send them now, they time out,
+        // ENABLE broacasts. If we try to send them now, they time out,
         // because the system isn't ready to handle them yet.
         mContext.registerReceiver(mBroadcastReceiver,
                 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
 
         // Register for configuration changes so we can update the names
         // of the widgets when the locale changes.
-        mContext.registerReceiver(mBroadcastReceiver,
-                new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
+        mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(
+                Intent.ACTION_CONFIGURATION_CHANGED), null, null);
 
         // Register for broadcasts about package install, etc., so we can
         // update the provider list.
@@ -216,216 +172,24 @@
         mContext.registerReceiver(mBroadcastReceiver, sdFilter);
     }
 
-    private void ensureStateLoadedLocked() {
-        if (!mStateLoaded) {
-            loadAppWidgetList();
-            loadStateLocked();
-            mStateLoaded = true;
-        }
+    @Override
+    public int allocateAppWidgetId(String packageName, int hostId) throws RemoteException {
+        return getImplForUser().allocateAppWidgetId(packageName, hostId);
     }
-
-    private void dumpProvider(Provider p, int index, PrintWriter pw) {
-        AppWidgetProviderInfo info = p.info;
-        pw.print("  ["); pw.print(index); pw.print("] provider ");
-                pw.print(info.provider.flattenToShortString());
-                pw.println(':');
-        pw.print("    min=("); pw.print(info.minWidth);
-                pw.print("x"); pw.print(info.minHeight);
-        pw.print(")   minResize=("); pw.print(info.minResizeWidth);
-                pw.print("x"); pw.print(info.minResizeHeight);
-                pw.print(") updatePeriodMillis=");
-                pw.print(info.updatePeriodMillis);
-                pw.print(" resizeMode=");
-                pw.print(info.resizeMode);
-                pw.print(" autoAdvanceViewId=");
-                pw.print(info.autoAdvanceViewId);
-                pw.print(" initialLayout=#");
-                pw.print(Integer.toHexString(info.initialLayout));
-                pw.print(" zombie="); pw.println(p.zombie);
-    }
-
-    private void dumpHost(Host host, int index, PrintWriter pw) {
-        pw.print("  ["); pw.print(index); pw.print("] hostId=");
-                pw.print(host.hostId); pw.print(' ');
-                pw.print(host.packageName); pw.print('/');
-        pw.print(host.uid); pw.println(':');
-        pw.print("    callbacks="); pw.println(host.callbacks);
-        pw.print("    instances.size="); pw.print(host.instances.size());
-                pw.print(" zombie="); pw.println(host.zombie);
-    }
-
-    private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
-        pw.print("  ["); pw.print(index); pw.print("] id=");
-                pw.println(id.appWidgetId);
-        pw.print("    hostId=");
-                pw.print(id.host.hostId); pw.print(' ');
-                pw.print(id.host.packageName); pw.print('/');
-                pw.println(id.host.uid);
-        if (id.provider != null) {
-            pw.print("    provider=");
-                    pw.println(id.provider.info.provider.flattenToShortString());
-        }
-        if (id.host != null) {
-            pw.print("    host.callbacks="); pw.println(id.host.callbacks);
-        }
-        if (id.views != null) {
-            pw.print("    views="); pw.println(id.views);
-        }
+    
+    @Override
+    public void deleteAppWidgetId(int appWidgetId) throws RemoteException {
+        getImplForUser().deleteAppWidgetId(appWidgetId);
     }
 
     @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 from from pid="
-                    + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid());
-            return;
-        }
-
-        synchronized (mAppWidgetIds) {
-            int N = mInstalledProviders.size();
-            pw.println("Providers:");
-            for (int i=0; i<N; i++) {
-                dumpProvider(mInstalledProviders.get(i), i, pw);
-            }
-
-            N = mAppWidgetIds.size();
-            pw.println(" ");
-            pw.println("AppWidgetIds:");
-            for (int i=0; i<N; i++) {
-                dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
-            }
-
-            N = mHosts.size();
-            pw.println(" ");
-            pw.println("Hosts:");
-            for (int i=0; i<N; i++) {
-                dumpHost(mHosts.get(i), i, pw);
-            }
-
-            N = mDeletedProviders.size();
-            pw.println(" ");
-            pw.println("Deleted Providers:");
-            for (int i=0; i<N; i++) {
-                dumpProvider(mDeletedProviders.get(i), i, pw);
-            }
-
-            N = mDeletedHosts.size();
-            pw.println(" ");
-            pw.println("Deleted Hosts:");
-            for (int i=0; i<N; i++) {
-                dumpHost(mDeletedHosts.get(i), i, pw);
-            }
-        }
+    public void deleteHost(int hostId) throws RemoteException {
+        getImplForUser().deleteHost(hostId);
     }
 
-    public int allocateAppWidgetId(String packageName, int hostId) {
-        int callingUid = enforceCallingUid(packageName);
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            int appWidgetId = mNextAppWidgetId++;
-
-            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
-
-            AppWidgetId id = new AppWidgetId();
-            id.appWidgetId = appWidgetId;
-            id.host = host;
-
-            host.instances.add(id);
-            mAppWidgetIds.add(id);
-
-            saveStateLocked();
-
-            return appWidgetId;
-        }
-    }
-
-    public void deleteAppWidgetId(int appWidgetId) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-            if (id != null) {
-                deleteAppWidgetLocked(id);
-                saveStateLocked();
-            }
-        }
-    }
-
-    public void deleteHost(int hostId) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            int callingUid = getCallingUid();
-            Host host = lookupHostLocked(callingUid, hostId);
-            if (host != null) {
-                deleteHostLocked(host);
-                saveStateLocked();
-            }
-        }
-    }
-
-    public void deleteAllHosts() {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            int callingUid = getCallingUid();
-            final int N = mHosts.size();
-            boolean changed = false;
-            for (int i=N-1; i>=0; i--) {
-                Host host = mHosts.get(i);
-                if (host.uid == callingUid) {
-                    deleteHostLocked(host);
-                    changed = true;
-                }
-            }
-            if (changed) {
-                saveStateLocked();
-            }
-        }
-    }
-
-    void deleteHostLocked(Host host) {
-        final int N = host.instances.size();
-        for (int i=N-1; i>=0; i--) {
-            AppWidgetId id = host.instances.get(i);
-            deleteAppWidgetLocked(id);
-        }
-        host.instances.clear();
-        mHosts.remove(host);
-        mDeletedHosts.add(host);
-        // it's gone or going away, abruptly drop the callback connection
-        host.callbacks = null;
-    }
-
-    void deleteAppWidgetLocked(AppWidgetId id) {
-        // We first unbind all services that are bound to this id
-        unbindAppWidgetRemoteViewsServicesLocked(id);
-
-        Host host = id.host;
-        host.instances.remove(id);
-        pruneHostLocked(host);
-
-        mAppWidgetIds.remove(id);
-
-        Provider p = id.provider;
-        if (p != null) {
-            p.instances.remove(id);
-            if (!p.zombie) {
-                // send the broacast saying that this appWidgetId has been deleted
-                Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
-                intent.setComponent(p.info.provider);
-                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
-                mContext.sendBroadcast(intent);
-                if (p.instances.size() == 0) {
-                    // cancel the future updates
-                    cancelBroadcasts(p);
-
-                    // send the broacast saying that the provider is not in use any more
-                    intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
-                    intent.setComponent(p.info.provider);
-                    mContext.sendBroadcast(intent);
-                }
-            }
-        }
+    @Override
+    public void deleteAllHosts() throws RemoteException {
+        getImplForUser().deleteAllHosts();
     }
 
     void cancelBroadcasts(Provider p) {
@@ -441,617 +205,58 @@
         }
     }
 
-    public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
-        mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
-                "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
-        
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mAppWidgetIds) {
-                ensureStateLoadedLocked();
-                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-                if (id == null) {
-                    throw new IllegalArgumentException("bad appWidgetId");
-                }
-                if (id.provider != null) {
-                    throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
-                            + id.provider.info.provider);
-                }
-                Provider p = lookupProviderLocked(provider);
-                if (p == null) {
-                    throw new IllegalArgumentException("not a appwidget provider: " + provider);
-                }
-                if (p.zombie) {
-                    throw new IllegalArgumentException("can't bind to a 3rd party provider in"
-                            + " safe mode: " + provider);
-                }
-    
-                id.provider = p;
-                p.instances.add(id);
-                int instancesSize = p.instances.size();
-                if (instancesSize == 1) {
-                    // tell the provider that it's ready
-                    sendEnableIntentLocked(p);
-                }
-    
-                // send an update now -- We need this update now, and just for this appWidgetId.
-                // It's less critical when the next one happens, so when we schdule the next one,
-                // we add updatePeriodMillis to its start time.  That time will have some slop,
-                // but that's okay.
-                sendUpdateIntentLocked(p, new int[] { appWidgetId });
-    
-                // schedule the future updates
-                registerForBroadcastsLocked(p, getAppWidgetIds(p));
-                saveStateLocked();
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
+    @Override
+    public void bindAppWidgetId(int appWidgetId, ComponentName provider) throws RemoteException {
+        getImplForUser().bindAppWidgetId(appWidgetId, provider);
     }
 
-    // Binds to a specific RemoteViewsService
-    public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-            if (id == null) {
-                throw new IllegalArgumentException("bad appWidgetId");
-            }
-            final ComponentName componentName = intent.getComponent();
-            try {
-                final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
-                        PackageManager.GET_PERMISSIONS);
-                if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
-                    throw new SecurityException("Selected service does not require "
-                            + android.Manifest.permission.BIND_REMOTEVIEWS
-                            + ": " + componentName);
-                }
-            } catch (PackageManager.NameNotFoundException e) {
-                throw new IllegalArgumentException("Unknown component " + componentName);
-            }
-
-            // If there is already a connection made for this service intent, then disconnect from
-            // that first.  (This does not allow multiple connections to the same service under
-            // the same key)
-            ServiceConnectionProxy conn = null;
-            FilterComparison fc = new FilterComparison(intent);
-            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
-            if (mBoundRemoteViewsServices.containsKey(key)) {
-                conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
-                conn.disconnect();
-                mContext.unbindService(conn);
-                mBoundRemoteViewsServices.remove(key);
-            }
-
-            // Bind to the RemoteViewsService (which will trigger a callback to the
-            // RemoteViewsAdapter.onServiceConnected())
-            final long token = Binder.clearCallingIdentity();
-            try {
-                conn = new ServiceConnectionProxy(key, connection);
-                mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
-                mBoundRemoteViewsServices.put(key, conn);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-
-            // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
-            // when we can call back to the RemoteViewsService later to destroy associated
-            // factories.
-            incrementAppWidgetServiceRefCount(appWidgetId, fc);
-        }
+    @Override
+    public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection)
+            throws RemoteException {
+        getImplForUser().bindRemoteViewsService(appWidgetId, intent, connection);
     }
 
-    // Unbinds from a specific RemoteViewsService
-    public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            // Unbind from the RemoteViewsService (which will trigger a callback to the bound
-            // RemoteViewsAdapter)
-            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
-                    new FilterComparison(intent));
-            if (mBoundRemoteViewsServices.containsKey(key)) {
-                // We don't need to use the appWidgetId until after we are sure there is something
-                // to unbind.  Note that this may mask certain issues with apps calling unbind()
-                // more than necessary.
-                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-                if (id == null) {
-                    throw new IllegalArgumentException("bad appWidgetId");
-                }
-
-                ServiceConnectionProxy conn =
-                    (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
-                conn.disconnect();
-                mContext.unbindService(conn);
-                mBoundRemoteViewsServices.remove(key);
-            } else {
-                Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
-            }
-        }
+    @Override
+    public int[] startListening(IAppWidgetHost host, String packageName, int hostId,
+            List<RemoteViews> updatedViews) throws RemoteException {
+        return getImplForUser().startListening(host, packageName, hostId, updatedViews);
     }
 
-    // Unbinds from a RemoteViewsService when we delete an app widget
-    private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
-        int appWidgetId = id.appWidgetId;
-        // Unbind all connections to Services bound to this AppWidgetId
-        Iterator<Pair<Integer, Intent.FilterComparison>> it =
-            mBoundRemoteViewsServices.keySet().iterator();
-        while (it.hasNext()) {
-            final Pair<Integer, Intent.FilterComparison> key = it.next();
-            if (key.first.intValue() == appWidgetId) {
-                final ServiceConnectionProxy conn = (ServiceConnectionProxy)
-                        mBoundRemoteViewsServices.get(key);
-                conn.disconnect();
-                mContext.unbindService(conn);
-                it.remove();
-            }
-        }
-
-        // Check if we need to destroy any services (if no other app widgets are
-        // referencing the same service)
-        decrementAppWidgetServiceRefCount(appWidgetId);
+    // TODO: Call this from PackageManagerService when a user is removed
+    public void removeUser(int userId) {
     }
 
-    // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
-    private void destroyRemoteViewsService(final Intent intent) {
-        final ServiceConnection conn = new ServiceConnection() {
-            @Override
-            public void onServiceConnected(ComponentName name, IBinder service) {
-                final IRemoteViewsFactory cb =
-                    IRemoteViewsFactory.Stub.asInterface(service);
-                try {
-                    cb.onDestroy(intent);
-                } catch (RemoteException e) {
-                    e.printStackTrace();
-                } catch (RuntimeException e) {
-                    e.printStackTrace();
-                }
-                mContext.unbindService(this);
-            }
-            @Override
-            public void onServiceDisconnected(android.content.ComponentName name) {
-                // Do nothing
-            }
-        };
-
-        // Bind to the service and remove the static intent->factory mapping in the
-        // RemoteViewsService.
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
-        } finally {
-            Binder.restoreCallingIdentity(token);
+    private AppWidgetServiceImpl getImplForUser() {
+        final int userId = Binder.getOrigCallingUser();
+        AppWidgetServiceImpl service = mAppWidgetServices.get(userId);
+        if (service == null) {
+            Slog.e(TAG, "Unable to find AppWidgetServiceImpl for the current user");
+            // TODO: Verify that it's a valid user
+            service = new AppWidgetServiceImpl(mContext, userId);
+            service.systemReady(mSafeMode);
+            // Assume that BOOT_COMPLETED was received, as this is a non-primary user.
+            service.sendInitialBroadcasts();
+            mAppWidgetServices.append(userId, service);
         }
+
+        return service;
     }
 
-    // Adds to the ref-count for a given RemoteViewsService intent
-    private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
-        HashSet<Integer> appWidgetIds = null;
-        if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
-            appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
-        } else {
-            appWidgetIds = new HashSet<Integer>();
-            mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
-        }
-        appWidgetIds.add(appWidgetId);
+    @Override
+    public int[] getAppWidgetIds(ComponentName provider) throws RemoteException {
+        return getImplForUser().getAppWidgetIds(provider);
     }
 
-    // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
-    // the ref-count reaches zero.
-    private void decrementAppWidgetServiceRefCount(int appWidgetId) {
-        Iterator<FilterComparison> it =
-            mRemoteViewsServicesAppWidgets.keySet().iterator();
-        while (it.hasNext()) {
-            final FilterComparison key = it.next();
-            final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
-            if (ids.remove(appWidgetId)) {
-                // If we have removed the last app widget referencing this service, then we
-                // should destroy it and remove it from this set
-                if (ids.isEmpty()) {
-                    destroyRemoteViewsService(key.getIntent());
-                    it.remove();
-                }
-            }
-        }
+    @Override
+    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) throws RemoteException {
+        return getImplForUser().getAppWidgetInfo(appWidgetId);
     }
 
-    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-            if (id != null && id.provider != null && !id.provider.zombie) {
-                return id.provider.info;
-            }
-            return null;
-        }
+    @Override
+    public RemoteViews getAppWidgetViews(int appWidgetId) throws RemoteException {
+        return getImplForUser().getAppWidgetViews(appWidgetId);
     }
 
-    public RemoteViews getAppWidgetViews(int appWidgetId) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-            if (id != null) {
-                return id.views;
-            }
-            return null;
-        }
-    }
-
-    public List<AppWidgetProviderInfo> getInstalledProviders() {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            final int N = mInstalledProviders.size();
-            ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
-            for (int i=0; i<N; i++) {
-                Provider p = mInstalledProviders.get(i);
-                if (!p.zombie) {
-                    result.add(p.info);
-                }
-            }
-            return result;
-        }
-    }
-
-    public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
-        if (appWidgetIds == null) {
-            return;
-        }
-        if (appWidgetIds.length == 0) {
-            return;
-        }
-        final int N = appWidgetIds.length;
-
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            for (int i=0; i<N; i++) {
-                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
-                updateAppWidgetInstanceLocked(id, views);
-            }
-        }
-    }
-
-    public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
-        if (appWidgetIds == null) {
-            return;
-        }
-        if (appWidgetIds.length == 0) {
-            return;
-        }
-        final int N = appWidgetIds.length;
-
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            for (int i=0; i<N; i++) {
-                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
-                updateAppWidgetInstanceLocked(id, views, true);
-            }
-        }
-    }
-
-    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
-        if (appWidgetIds == null) {
-            return;
-        }
-        if (appWidgetIds.length == 0) {
-            return;
-        }
-        final int N = appWidgetIds.length;
-
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            for (int i=0; i<N; i++) {
-                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
-                notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
-            }
-        }
-    }
-
-    public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            Provider p = lookupProviderLocked(provider);
-            if (p == null) {
-                Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
-                return;
-            }
-            ArrayList<AppWidgetId> instances = p.instances;
-            final int callingUid = getCallingUid();
-            final int N = instances.size();
-            for (int i=0; i<N; i++) {
-                AppWidgetId id = instances.get(i);
-                if (canAccessAppWidgetId(id, callingUid)) {
-                    updateAppWidgetInstanceLocked(id, views);
-                }
-            }
-        }
-    }
-
-    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
-        updateAppWidgetInstanceLocked(id, views, false);
-    }
-
-    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
-        // allow for stale appWidgetIds and other badness
-        // lookup also checks that the calling process can access the appWidgetId
-        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
-        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
-
-            // We do not want to save this RemoteViews
-            if (!isPartialUpdate) id.views = views;
-
-            // is anyone listening?
-            if (id.host.callbacks != null) {
-                try {
-                    // the lock is held, but this is a oneway call
-                    id.host.callbacks.updateAppWidget(id.appWidgetId, views);
-                } catch (RemoteException e) {
-                    // It failed; remove the callback. No need to prune because
-                    // we know that this host is still referenced by this instance.
-                    id.host.callbacks = null;
-                }
-            }
-        }
-    }
-
-    void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
-        // allow for stale appWidgetIds and other badness
-        // lookup also checks that the calling process can access the appWidgetId
-        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
-        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
-            // is anyone listening?
-            if (id.host.callbacks != null) {
-                try {
-                    // the lock is held, but this is a oneway call
-                    id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
-                } catch (RemoteException e) {
-                    // It failed; remove the callback. No need to prune because
-                    // we know that this host is still referenced by this instance.
-                    id.host.callbacks = null;
-                }
-            }
-
-            // If the host is unavailable, then we call the associated
-            // RemoteViewsFactory.onDataSetChanged() directly
-            if (id.host.callbacks == null) {
-                Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
-                for (FilterComparison key : keys) {
-                    if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
-                        Intent intent = key.getIntent();
-
-                        final ServiceConnection conn = new ServiceConnection() {
-                            @Override
-                            public void onServiceConnected(ComponentName name, IBinder service) {
-                                IRemoteViewsFactory cb =
-                                    IRemoteViewsFactory.Stub.asInterface(service);
-                                try {
-                                    cb.onDataSetChangedAsync();
-                                } catch (RemoteException e) {
-                                    e.printStackTrace();
-                                } catch (RuntimeException e) {
-                                    e.printStackTrace();
-                                }
-                                mContext.unbindService(this);
-                            }
-                            @Override
-                            public void onServiceDisconnected(android.content.ComponentName name) {
-                                // Do nothing
-                            }
-                        };
-
-                        // Bind to the service and call onDataSetChanged()
-                        final long token = Binder.clearCallingIdentity();
-                        try {
-                            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
-                        } finally {
-                            Binder.restoreCallingIdentity(token);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
-            List<RemoteViews> updatedViews) {
-        int callingUid = enforceCallingUid(packageName);
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
-            host.callbacks = callbacks;
-
-            updatedViews.clear();
-
-            ArrayList<AppWidgetId> instances = host.instances;
-            int N = instances.size();
-            int[] updatedIds = new int[N];
-            for (int i=0; i<N; i++) {
-                AppWidgetId id = instances.get(i);
-                updatedIds[i] = id.appWidgetId;
-                updatedViews.add(id.views);
-            }
-            return updatedIds;
-        }
-    }
-
-    public void stopListening(int hostId) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            Host host = lookupHostLocked(getCallingUid(), hostId);
-            if (host != null) {
-                host.callbacks = null;
-                pruneHostLocked(host);
-            }
-        }
-    }
-
-    boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
-        if (id.host.uid == callingUid) {
-            // Apps hosting the AppWidget have access to it.
-            return true;
-        }
-        if (id.provider != null && id.provider.uid == callingUid) {
-            // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
-            return true;
-        }
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
-                == PackageManager.PERMISSION_GRANTED) {
-            // Apps that can bind have access to all appWidgetIds.
-            return true;
-        }
-        // Nobody else can access it.
-        return false;
-    }
-
-   AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
-        int callingUid = getCallingUid();
-        final int N = mAppWidgetIds.size();
-        for (int i=0; i<N; i++) {
-            AppWidgetId id = mAppWidgetIds.get(i);
-            if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
-                return id;
-            }
-        }
-        return null;
-    }
-
-    Provider lookupProviderLocked(ComponentName provider) {
-        final int N = mInstalledProviders.size();
-        for (int i=0; i<N; i++) {
-            Provider p = mInstalledProviders.get(i);
-            if (p.info.provider.equals(provider)) {
-                return p;
-            }
-        }
-        return null;
-    }
-
-    Host lookupHostLocked(int uid, int hostId) {
-        final int N = mHosts.size();
-        for (int i=0; i<N; i++) {
-            Host h = mHosts.get(i);
-            if (h.uid == uid && h.hostId == hostId) {
-                return h;
-            }
-        }
-        return null;
-    }
-
-    Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
-        final int N = mHosts.size();
-        for (int i=0; i<N; i++) {
-            Host h = mHosts.get(i);
-            if (h.hostId == hostId && h.packageName.equals(packageName)) {
-                return h;
-            }
-        }
-        Host host = new Host();
-        host.packageName = packageName;
-        host.uid = uid;
-        host.hostId = hostId;
-        mHosts.add(host);
-        return host;
-    }
-
-    void pruneHostLocked(Host host) {
-        if (host.instances.size() == 0 && host.callbacks == null) {
-            mHosts.remove(host);
-        }
-    }
-
-    void loadAppWidgetList() {
-        PackageManager pm = mPackageManager;
-
-        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
-        List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
-                PackageManager.GET_META_DATA);
-
-        final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
-        for (int i=0; i<N; i++) {
-            ResolveInfo ri = broadcastReceivers.get(i);
-            addProviderLocked(ri);
-        }
-    }
-
-    boolean addProviderLocked(ResolveInfo ri) {
-        if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
-            return false;
-        }
-        if (!ri.activityInfo.isEnabled()) {
-            return false;
-        }
-        Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
-                    ri.activityInfo.name), ri);
-        if (p != null) {
-            mInstalledProviders.add(p);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    void removeProviderLocked(int index, Provider p) {
-        int N = p.instances.size();
-        for (int i=0; i<N; i++) {
-            AppWidgetId id = p.instances.get(i);
-            // Call back with empty RemoteViews
-            updateAppWidgetInstanceLocked(id, null);
-            // Stop telling the host about updates for this from now on
-            cancelBroadcasts(p);
-            // clear out references to this appWidgetId
-            id.host.instances.remove(id);
-            mAppWidgetIds.remove(id);
-            id.provider = null;
-            pruneHostLocked(id.host);
-            id.host = null;
-        }
-        p.instances.clear();
-        mInstalledProviders.remove(index);
-        mDeletedProviders.add(p);
-        // no need to send the DISABLE broadcast, since the receiver is gone anyway
-        cancelBroadcasts(p);
-    }
-
-    void sendEnableIntentLocked(Provider p) {
-        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
-        intent.setComponent(p.info.provider);
-        mContext.sendBroadcast(intent);
-    }
-
-    void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
-        if (appWidgetIds != null && appWidgetIds.length > 0) {
-            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
-            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
-            intent.setComponent(p.info.provider);
-            mContext.sendBroadcast(intent);
-        }
-    }
-
-    void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
-        if (p.info.updatePeriodMillis > 0) {
-            // if this is the first instance, set the alarm.  otherwise,
-            // rely on the fact that we've already set it and that
-            // PendingIntent.getBroadcast will update the extras.
-            boolean alreadyRegistered = p.broadcast != null;
-            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
-            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
-            intent.setComponent(p.info.provider);
-            long token = Binder.clearCallingIdentity();
-            try {
-                p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
-                        PendingIntent.FLAG_UPDATE_CURRENT);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-            if (!alreadyRegistered) {
-                long period = p.info.updatePeriodMillis;
-                if (period < MIN_UPDATE_PERIOD) {
-                    period = MIN_UPDATE_PERIOD;
-                }
-                mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                        SystemClock.elapsedRealtime() + period, period, p.broadcast);
-            }
-        }
-    }
-    
     static int[] getAppWidgetIds(Provider p) {
         int instancesSize = p.instances.size();
         int appWidgetIds[] = new int[instancesSize];
@@ -1060,570 +265,70 @@
         }
         return appWidgetIds;
     }
-    
-    public int[] getAppWidgetIds(ComponentName provider) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            Provider p = lookupProviderLocked(provider);
-            if (p != null && getCallingUid() == p.uid) {
-                return getAppWidgetIds(p);                
-            } else {
-                return new int[0];
-            }
-        }
+
+    @Override
+    public List<AppWidgetProviderInfo> getInstalledProviders() throws RemoteException {
+        return getImplForUser().getInstalledProviders();
     }
 
-    private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
-        Provider p = null;
-
-        ActivityInfo activityInfo = ri.activityInfo;
-        XmlResourceParser parser = null;
-        try {
-            parser = activityInfo.loadXmlMetaData(mPackageManager,
-                    AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
-            if (parser == null) {
-                Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
-                        + "AppWidget provider '" + component + '\'');
-                return null;
-            }
-
-            AttributeSet attrs = Xml.asAttributeSet(parser);
-
-            int type;
-            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
-                    && type != XmlPullParser.START_TAG) {
-                // drain whitespace, comments, etc.
-            }
-
-            String nodeName = parser.getName();
-            if (!"appwidget-provider".equals(nodeName)) {
-                Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
-                        + " AppWidget provider '" + component + '\'');
-                return null;
-            }
-
-            p = new Provider();
-            AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
-            info.provider = component;
-            p.uid = activityInfo.applicationInfo.uid;
-
-            Resources res = mPackageManager.getResourcesForApplication(
-                    activityInfo.applicationInfo);
-
-            TypedArray sa = res.obtainAttributes(attrs,
-                    com.android.internal.R.styleable.AppWidgetProviderInfo);
-
-            // These dimensions has to be resolved in the application's context.
-            // We simply send back the raw complex data, which will be
-            // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
-            TypedValue value = sa.peekValue(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
-            info.minWidth = value != null ? value.data : 0; 
-            value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
-            info.minHeight = value != null ? value.data : 0;
-            value = sa.peekValue(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
-            info.minResizeWidth = value != null ? value.data : info.minWidth;
-            value = sa.peekValue(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
-            info.minResizeHeight = value != null ? value.data : info.minHeight;
-
-            info.updatePeriodMillis = sa.getInt(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
-            info.initialLayout = sa.getResourceId(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
-            String className = sa.getString(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
-            if (className != null) {
-                info.configure = new ComponentName(component.getPackageName(), className);
-            }
-            info.label = activityInfo.loadLabel(mPackageManager).toString();
-            info.icon = ri.getIconResource();
-            info.previewImage = sa.getResourceId(
-            		com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
-            info.autoAdvanceViewId = sa.getResourceId(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
-            info.resizeMode = sa.getInt(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
-                    AppWidgetProviderInfo.RESIZE_NONE);
-
-            sa.recycle();
-        } catch (Exception e) {
-            // Ok to catch Exception here, because anything going wrong because
-            // of what a client process passes to us should not be fatal for the
-            // system process.
-            Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
-            return null;
-        } finally {
-            if (parser != null) parser.close();
-        }
-        return p;
+    @Override
+    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId)
+            throws RemoteException {
+        getImplForUser().notifyAppWidgetViewDataChanged(appWidgetIds, viewId);
     }
 
-    int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
-        PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
-        if (pkgInfo == null || pkgInfo.applicationInfo == null) {
-            throw new PackageManager.NameNotFoundException();
-        }
-        return pkgInfo.applicationInfo.uid;
+    @Override
+    public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views)
+            throws RemoteException {
+        getImplForUser().partiallyUpdateAppWidgetIds(appWidgetIds, views);
     }
 
-    int enforceCallingUid(String packageName) throws IllegalArgumentException {
-        int callingUid = getCallingUid();
-        int packageUid;
-        try {
-            packageUid = getUidForPackage(packageName);
-        } catch (PackageManager.NameNotFoundException ex) {
-            throw new IllegalArgumentException("packageName and uid don't match packageName="
-                    + packageName);
-        }
-        if (callingUid != packageUid) {
-            throw new IllegalArgumentException("packageName and uid don't match packageName="
-                    + packageName);
-        }
-        return callingUid;
+    @Override
+    public void stopListening(int hostId) throws RemoteException {
+        getImplForUser().stopListening(hostId);
     }
 
-    void sendInitialBroadcasts() {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            final int N = mInstalledProviders.size();
-            for (int i=0; i<N; i++) {
-                Provider p = mInstalledProviders.get(i);
-                if (p.instances.size() > 0) {
-                    sendEnableIntentLocked(p);
-                    int[] appWidgetIds = getAppWidgetIds(p);
-                    sendUpdateIntentLocked(p, appWidgetIds);
-                    registerForBroadcastsLocked(p, appWidgetIds);
-                }
-            }
-        }
+    @Override
+    public void unbindRemoteViewsService(int appWidgetId, Intent intent) throws RemoteException {
+        getImplForUser().unbindRemoteViewsService(appWidgetId, intent);
     }
 
-    // only call from initialization -- it assumes that the data structures are all empty
-    void loadStateLocked() {
-        AtomicFile file = savedStateFile();
-        try {
-            FileInputStream stream = file.openRead();
-            readStateFromFileLocked(stream);
-
-            if (stream != null) {
-                try {
-                    stream.close();
-                } catch (IOException e) {
-                    Slog.w(TAG, "Failed to close state FileInputStream " + e);
-                }
-            }
-        } catch (FileNotFoundException e) {
-            Slog.w(TAG, "Failed to read state: " + e);
-        }
+    @Override
+    public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) throws RemoteException {
+        getImplForUser().updateAppWidgetIds(appWidgetIds, views);
     }
 
-    void saveStateLocked() {
-        AtomicFile file = savedStateFile();
-        FileOutputStream stream;
-        try {
-            stream = file.startWrite();
-            if (writeStateToFileLocked(stream)) {
-                file.finishWrite(stream);
-            } else {
-                file.failWrite(stream);
-                Slog.w(TAG, "Failed to save state, restoring backup.");
-            }
-        } catch (IOException e) {
-            Slog.w(TAG, "Failed open state file for write: " + e);
-        }
+    @Override
+    public void updateAppWidgetProvider(ComponentName provider, RemoteViews views)
+            throws RemoteException {
+        getImplForUser().updateAppWidgetProvider(provider, views);
     }
 
-    boolean writeStateToFileLocked(FileOutputStream stream) {
-        int N;
-
-        try {
-            XmlSerializer out = new FastXmlSerializer();
-            out.setOutput(stream, "utf-8");
-            out.startDocument(null, true);
-            out.startTag(null, "gs");
-
-            int providerIndex = 0;
-            N = mInstalledProviders.size();
-            for (int i=0; i<N; i++) {
-                Provider p = mInstalledProviders.get(i);
-                if (p.instances.size() > 0) {
-                    out.startTag(null, "p");
-                    out.attribute(null, "pkg", p.info.provider.getPackageName());
-                    out.attribute(null, "cl", p.info.provider.getClassName());
-                    out.endTag(null, "p");
-                    p.tag = providerIndex;
-                    providerIndex++;
-                }
-            }
-
-            N = mHosts.size();
-            for (int i=0; i<N; i++) {
-                Host host = mHosts.get(i);
-                out.startTag(null, "h");
-                out.attribute(null, "pkg", host.packageName);
-                out.attribute(null, "id", Integer.toHexString(host.hostId));
-                out.endTag(null, "h");
-                host.tag = i;
-            }
-
-            N = mAppWidgetIds.size();
-            for (int i=0; i<N; i++) {
-                AppWidgetId id = mAppWidgetIds.get(i);
-                out.startTag(null, "g");
-                out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
-                out.attribute(null, "h", Integer.toHexString(id.host.tag));
-                if (id.provider != null) {
-                    out.attribute(null, "p", Integer.toHexString(id.provider.tag));
-                }
-                out.endTag(null, "g");
-            }
-
-            out.endTag(null, "gs");
-
-            out.endDocument();
-            return true;
-        } catch (IOException e) {
-            Slog.w(TAG, "Failed to write state: " + e);
-            return false;
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        // Dump the state of all the app widget providers
+        for (int i = 0; i < mAppWidgetServices.size(); i++) {
+            AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
+            service.dump(fd, pw, args);
         }
     }
 
-    void readStateFromFileLocked(FileInputStream stream) {
-        boolean success = false;
-
-        try {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(stream, null);
-
-            int type;
-            int providerIndex = 0;
-            HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
-            do {
-                type = parser.next();
-                if (type == XmlPullParser.START_TAG) {
-                    String tag = parser.getName();
-                    if ("p".equals(tag)) {
-                        // TODO: do we need to check that this package has the same signature
-                        // as before?
-                        String pkg = parser.getAttributeValue(null, "pkg");
-                        String cl = parser.getAttributeValue(null, "cl");
-
-                        final PackageManager packageManager = mContext.getPackageManager();
-                        try {
-                            packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
-                        } catch (PackageManager.NameNotFoundException e) {
-                            String[] pkgs = packageManager.currentToCanonicalPackageNames(
-                                    new String[] { pkg });
-                            pkg = pkgs[0];
-                        }
-
-                        Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
-                        if (p == null && mSafeMode) {
-                            // if we're in safe mode, make a temporary one
-                            p = new Provider();
-                            p.info = new AppWidgetProviderInfo();
-                            p.info.provider = new ComponentName(pkg, cl);
-                            p.zombie = true;
-                            mInstalledProviders.add(p);
-                        }
-                        if (p != null) {
-                            // if it wasn't uninstalled or something
-                            loadedProviders.put(providerIndex, p);
-                        }
-                        providerIndex++;
-                    }
-                    else if ("h".equals(tag)) {
-                        Host host = new Host();
-
-                        // TODO: do we need to check that this package has the same signature
-                        // as before?
-                        host.packageName = parser.getAttributeValue(null, "pkg");
-                        try {
-                            host.uid = getUidForPackage(host.packageName);
-                        } catch (PackageManager.NameNotFoundException ex) {
-                            host.zombie = true;
-                        }
-                        if (!host.zombie || mSafeMode) {
-                            // In safe mode, we don't discard the hosts we don't recognize
-                            // so that they're not pruned from our list.  Otherwise, we do.
-                            host.hostId = Integer.parseInt(
-                                    parser.getAttributeValue(null, "id"), 16);
-                            mHosts.add(host);
-                        }
-                    }
-                    else if ("g".equals(tag)) {
-                        AppWidgetId id = new AppWidgetId();
-                        id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
-                        if (id.appWidgetId >= mNextAppWidgetId) {
-                            mNextAppWidgetId = id.appWidgetId + 1;
-                        }
-
-                        String providerString = parser.getAttributeValue(null, "p");
-                        if (providerString != null) {
-                            // there's no provider if it hasn't been bound yet.
-                            // maybe we don't have to save this, but it brings the system
-                            // to the state it was in.
-                            int pIndex = Integer.parseInt(providerString, 16);
-                            id.provider = loadedProviders.get(pIndex);
-                            if (false) {
-                                Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
-                                        + pIndex + " which is " + id.provider);
-                            }
-                            if (id.provider == null) {
-                                // This provider is gone.  We just let the host figure out
-                                // that this happened when it fails to load it.
-                                continue;
-                            }
-                        }
-
-                        int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
-                        id.host = mHosts.get(hIndex);
-                        if (id.host == null) {
-                            // This host is gone.
-                            continue;
-                        }
-
-                        if (id.provider != null) {
-                            id.provider.instances.add(id);
-                        }
-                        id.host.instances.add(id);
-                        mAppWidgetIds.add(id);
-                    }
-                }
-            } while (type != XmlPullParser.END_DOCUMENT);
-            success = true;
-        } catch (NullPointerException e) {
-            Slog.w(TAG, "failed parsing " + e);
-        } catch (NumberFormatException e) {
-            Slog.w(TAG, "failed parsing " + e);
-        } catch (XmlPullParserException e) {
-            Slog.w(TAG, "failed parsing " + e);
-        } catch (IOException e) {
-            Slog.w(TAG, "failed parsing " + e);
-        } catch (IndexOutOfBoundsException e) {
-            Slog.w(TAG, "failed parsing " + e);
-        }
-
-        if (success) {
-            // delete any hosts that didn't manage to get connected (should happen)
-            // if it matters, they'll be reconnected.
-            for (int i=mHosts.size()-1; i>=0; i--) {
-                pruneHostLocked(mHosts.get(i));
-            }
-        } else {
-            // failed reading, clean up
-            Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
-
-            mAppWidgetIds.clear();
-            mHosts.clear();
-            final int N = mInstalledProviders.size();
-            for (int i=0; i<N; i++) {
-                mInstalledProviders.get(i).instances.clear();
-            }
-        }
-    }
-
-    AtomicFile savedStateFile() {
-        return new AtomicFile(new File("/data/system/" + SETTINGS_FILENAME));
-    }
-
     BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            //Slog.d(TAG, "received " + action);
+            // Slog.d(TAG, "received " + action);
             if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
-                sendInitialBroadcasts();
+                getImplForUser().sendInitialBroadcasts();
             } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
-                Locale revised = Locale.getDefault();
-                if (revised == null || mLocale == null ||
-                    !(revised.equals(mLocale))) {
-                    mLocale = revised;
-
-                    synchronized (mAppWidgetIds) {
-                        ensureStateLoadedLocked();
-                        int N = mInstalledProviders.size();
-                        for (int i=N-1; i>=0; i--) {
-                            Provider p = mInstalledProviders.get(i);
-                            String pkgName = p.info.provider.getPackageName();
-                            updateProvidersForPackageLocked(pkgName);
-                        }
-                        saveStateLocked();
-                    }
+                for (int i = 0; i < mAppWidgetServices.size(); i++) {
+                    AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
+                    service.onConfigurationChanged();
                 }
             } else {
-                boolean added = false;
-                boolean changed = false;
-                String pkgList[] = null;
-                if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
-                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-                    added = true;
-                } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
-                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-                    added = false;
-                } else  {
-                    Uri uri = intent.getData();
-                    if (uri == null) {
-                        return;
-                    }
-                    String pkgName = uri.getSchemeSpecificPart();
-                    if (pkgName == null) {
-                        return;
-                    }
-                    pkgList = new String[] { pkgName };
-                    added = Intent.ACTION_PACKAGE_ADDED.equals(action);
-                    changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
-                }
-                if (pkgList == null || pkgList.length == 0) {
-                    return;
-                }
-                if (added || changed) {
-                    synchronized (mAppWidgetIds) {
-                        ensureStateLoadedLocked();
-                        Bundle extras = intent.getExtras();
-                        if (changed || (extras != null &&
-                                    extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
-                            for (String pkgName : pkgList) {
-                                // The package was just upgraded
-                                updateProvidersForPackageLocked(pkgName);
-                            }
-                        } else {
-                            // The package was just added
-                            for (String pkgName : pkgList) {
-                                addProvidersForPackageLocked(pkgName);
-                            }
-                        }
-                        saveStateLocked();
-                    }
-                } else {
-                    Bundle extras = intent.getExtras();
-                    if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
-                        // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
-                    } else {
-                        synchronized (mAppWidgetIds) {
-                            ensureStateLoadedLocked();
-                            for (String pkgName : pkgList) {
-                                removeProvidersForPackageLocked(pkgName);
-                                saveStateLocked();
-                            }
-                        }
-                    }
-                }
+                // TODO: Verify that this only needs to be delivered for the related user and not
+                // all the users
+                getImplForUser().onBroadcastReceived(intent);
             }
         }
     };
-
-    void addProvidersForPackageLocked(String pkgName) {
-        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
-        intent.setPackage(pkgName);
-        List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
-                PackageManager.GET_META_DATA);
-
-        final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
-        for (int i=0; i<N; i++) {
-            ResolveInfo ri = broadcastReceivers.get(i);
-            ActivityInfo ai = ri.activityInfo;
-            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
-                continue;
-            }
-            if (pkgName.equals(ai.packageName)) {
-                addProviderLocked(ri);
-            }
-        }
-    }
-
-    void updateProvidersForPackageLocked(String pkgName) {
-        HashSet<String> keep = new HashSet<String>();
-        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
-        intent.setPackage(pkgName);
-        List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
-                PackageManager.GET_META_DATA);
-
-        // add the missing ones and collect which ones to keep
-        int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
-        for (int i=0; i<N; i++) {
-            ResolveInfo ri = broadcastReceivers.get(i);
-            ActivityInfo ai = ri.activityInfo;
-            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
-                continue;
-            }
-            if (pkgName.equals(ai.packageName)) {
-                ComponentName component = new ComponentName(ai.packageName, ai.name);
-                Provider p = lookupProviderLocked(component);
-                if (p == null) {
-                    if (addProviderLocked(ri)) {
-                        keep.add(ai.name);
-                    }
-                } else {
-                    Provider parsed = parseProviderInfoXml(component, ri);
-                    if (parsed != null) {
-                        keep.add(ai.name);
-                        // Use the new AppWidgetProviderInfo.
-                        p.info = parsed.info;
-                        // If it's enabled
-                        final int M = p.instances.size();
-                        if (M > 0) {
-                            int[] appWidgetIds = getAppWidgetIds(p);
-                            // Reschedule for the new updatePeriodMillis (don't worry about handling
-                            // it specially if updatePeriodMillis didn't change because we just sent
-                            // an update, and the next one will be updatePeriodMillis from now).
-                            cancelBroadcasts(p);
-                            registerForBroadcastsLocked(p, appWidgetIds);
-                            // If it's currently showing, call back with the new AppWidgetProviderInfo.
-                            for (int j=0; j<M; j++) {
-                                AppWidgetId id = p.instances.get(j);
-                                id.views = null;
-                                if (id.host != null && id.host.callbacks != null) {
-                                    try {
-                                        id.host.callbacks.providerChanged(id.appWidgetId, p.info);
-                                    } catch (RemoteException ex) {
-                                        // It failed; remove the callback. No need to prune because
-                                        // we know that this host is still referenced by this
-                                        // instance.
-                                        id.host.callbacks = null;
-                                    }
-                                }
-                            }
-                            // Now that we've told the host, push out an update.
-                            sendUpdateIntentLocked(p, appWidgetIds);
-                        }
-                    }
-                }
-            }
-        }
-
-        // prune the ones we don't want to keep
-        N = mInstalledProviders.size();
-        for (int i=N-1; i>=0; i--) {
-            Provider p = mInstalledProviders.get(i);
-            if (pkgName.equals(p.info.provider.getPackageName())
-                    && !keep.contains(p.info.provider.getClassName())) {
-                removeProviderLocked(i, p);
-            }
-        }
-    }
-
-    void removeProvidersForPackageLocked(String pkgName) {
-        int N = mInstalledProviders.size();
-        for (int i=N-1; i>=0; i--) {
-            Provider p = mInstalledProviders.get(i);
-            if (pkgName.equals(p.info.provider.getPackageName())) {
-                removeProviderLocked(i, p);
-            }
-        }
-
-        // Delete the hosts for this package too
-        //
-        // By now, we have removed any AppWidgets that were in any hosts here,
-        // so we don't need to worry about sending DISABLE broadcasts to them.
-        N = mHosts.size();
-        for (int i=N-1; i>=0; i--) {
-            Host host = mHosts.get(i);
-            if (pkgName.equals(host.packageName)) {
-                deleteHostLocked(host);
-            }
-        }
-    }
 }
-
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
new file mode 100644
index 0000000..250386f
--- /dev/null
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -0,0 +1,1606 @@
+/*
+ * Copyright (C) 2011 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;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.Intent.FilterComparison;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserId;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.util.Xml;
+import android.widget.RemoteViews;
+
+import com.android.internal.appwidget.IAppWidgetHost;
+import com.android.internal.os.AtomicFile;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.widget.IRemoteViewsAdapterConnection;
+import com.android.internal.widget.IRemoteViewsFactory;
+import com.android.server.am.ActivityManagerService;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+class AppWidgetServiceImpl {
+
+    private static final String TAG = "AppWidgetServiceImpl";
+    private static final String SETTINGS_FILENAME = "appwidgets.xml";
+    private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
+
+    /*
+     * When identifying a Host or Provider based on the calling process, use the uid field. When
+     * identifying a Host or Provider based on a package manager broadcast, use the package given.
+     */
+
+    static class Provider {
+        int uid;
+        AppWidgetProviderInfo info;
+        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
+        PendingIntent broadcast;
+        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
+
+        int tag; // for use while saving state (the index)
+    }
+
+    static class Host {
+        int uid;
+        int hostId;
+        String packageName;
+        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
+        IAppWidgetHost callbacks;
+        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
+
+        int tag; // for use while saving state (the index)
+    }
+
+    static class AppWidgetId {
+        int appWidgetId;
+        Provider provider;
+        RemoteViews views;
+        Host host;
+    }
+
+    /**
+     * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
+     * needs to be a static inner class since a reference to the ServiceConnection is held globally
+     * and may lead us to leak AppWidgetService instances (if there were more than one).
+     */
+    static class ServiceConnectionProxy implements ServiceConnection {
+        private final IBinder mConnectionCb;
+
+        ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
+            mConnectionCb = connectionCb;
+        }
+
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
+                    .asInterface(mConnectionCb);
+            try {
+                cb.onServiceConnected(service);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            disconnect();
+        }
+
+        public void disconnect() {
+            final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
+                    .asInterface(mConnectionCb);
+            try {
+                cb.onServiceDisconnected();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    // Manages active connections to RemoteViewsServices
+    private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer, FilterComparison>, ServiceConnection>();
+    // Manages persistent references to RemoteViewsServices from different App Widgets
+    private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
+
+    Context mContext;
+    Locale mLocale;
+    PackageManager mPackageManager;
+    AlarmManager mAlarmManager;
+    ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
+    int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
+    final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
+    ArrayList<Host> mHosts = new ArrayList<Host>();
+    boolean mSafeMode;
+    int mUserId;
+    boolean mStateLoaded;
+
+    // These are for debugging only -- widgets are going missing in some rare instances
+    ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
+    ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
+
+    AppWidgetServiceImpl(Context context, int userId) {
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+        mUserId = userId;
+    }
+
+    public void systemReady(boolean safeMode) {
+        mSafeMode = safeMode;
+
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+        }
+    }
+
+    void onConfigurationChanged() {
+        Locale revised = Locale.getDefault();
+        if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
+            mLocale = revised;
+
+            synchronized (mAppWidgetIds) {
+                ensureStateLoadedLocked();
+                int N = mInstalledProviders.size();
+                for (int i = N - 1; i >= 0; i--) {
+                    Provider p = mInstalledProviders.get(i);
+                    String pkgName = p.info.provider.getPackageName();
+                    updateProvidersForPackageLocked(pkgName);
+                }
+                saveStateLocked();
+            }
+        }
+    }
+
+    void onBroadcastReceived(Intent intent) {
+        final String action = intent.getAction();
+        boolean added = false;
+        boolean changed = false;
+        String pkgList[] = null;
+        if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+            pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            added = true;
+        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+            pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            added = false;
+        } else {
+            Uri uri = intent.getData();
+            if (uri == null) {
+                return;
+            }
+            String pkgName = uri.getSchemeSpecificPart();
+            if (pkgName == null) {
+                return;
+            }
+            pkgList = new String[] { pkgName };
+            added = Intent.ACTION_PACKAGE_ADDED.equals(action);
+            changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
+        }
+        if (pkgList == null || pkgList.length == 0) {
+            return;
+        }
+        if (added || changed) {
+            synchronized (mAppWidgetIds) {
+                ensureStateLoadedLocked();
+                Bundle extras = intent.getExtras();
+                if (changed
+                        || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
+                    for (String pkgName : pkgList) {
+                        // The package was just upgraded
+                        updateProvidersForPackageLocked(pkgName);
+                    }
+                } else {
+                    // The package was just added
+                    for (String pkgName : pkgList) {
+                        addProvidersForPackageLocked(pkgName);
+                    }
+                }
+                saveStateLocked();
+            }
+        } else {
+            Bundle extras = intent.getExtras();
+            if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
+                // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
+            } else {
+                synchronized (mAppWidgetIds) {
+                    ensureStateLoadedLocked();
+                    for (String pkgName : pkgList) {
+                        removeProvidersForPackageLocked(pkgName);
+                        saveStateLocked();
+                    }
+                }
+            }
+        }
+    }
+
+    private void dumpProvider(Provider p, int index, PrintWriter pw) {
+        AppWidgetProviderInfo info = p.info;
+        pw.print("  ["); pw.print(index); pw.print("] provider ");
+                pw.print(info.provider.flattenToShortString());
+                pw.println(':');
+        pw.print("    min=("); pw.print(info.minWidth);
+                pw.print("x"); pw.print(info.minHeight);
+        pw.print(")   minResize=("); pw.print(info.minResizeWidth);
+                pw.print("x"); pw.print(info.minResizeHeight);
+                pw.print(") updatePeriodMillis=");
+                pw.print(info.updatePeriodMillis);
+                pw.print(" resizeMode=");
+                pw.print(info.resizeMode);
+                pw.print(" autoAdvanceViewId=");
+                pw.print(info.autoAdvanceViewId);
+                pw.print(" initialLayout=#");
+                pw.print(Integer.toHexString(info.initialLayout));
+                pw.print(" zombie="); pw.println(p.zombie);
+    }
+
+    private void dumpHost(Host host, int index, PrintWriter pw) {
+        pw.print("  ["); pw.print(index); pw.print("] hostId=");
+                pw.print(host.hostId); pw.print(' ');
+                pw.print(host.packageName); pw.print('/');
+        pw.print(host.uid); pw.println(':');
+        pw.print("    callbacks="); pw.println(host.callbacks);
+        pw.print("    instances.size="); pw.print(host.instances.size());
+                pw.print(" zombie="); pw.println(host.zombie);
+    }
+
+    private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
+        pw.print("  ["); pw.print(index); pw.print("] id=");
+                pw.println(id.appWidgetId);
+        pw.print("    hostId=");
+                pw.print(id.host.hostId); pw.print(' ');
+                pw.print(id.host.packageName); pw.print('/');
+                pw.println(id.host.uid);
+        if (id.provider != null) {
+            pw.print("    provider=");
+                    pw.println(id.provider.info.provider.flattenToShortString());
+        }
+        if (id.host != null) {
+            pw.print("    host.callbacks="); pw.println(id.host.callbacks);
+        }
+        if (id.views != null) {
+            pw.print("    views="); pw.println(id.views);
+        }
+    }
+
+    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 from from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid());
+            return;
+        }
+
+        synchronized (mAppWidgetIds) {
+            int N = mInstalledProviders.size();
+            pw.println("Providers:");
+            for (int i=0; i<N; i++) {
+                dumpProvider(mInstalledProviders.get(i), i, pw);
+            }
+
+            N = mAppWidgetIds.size();
+            pw.println(" ");
+            pw.println("AppWidgetIds:");
+            for (int i=0; i<N; i++) {
+                dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
+            }
+
+            N = mHosts.size();
+            pw.println(" ");
+            pw.println("Hosts:");
+            for (int i=0; i<N; i++) {
+                dumpHost(mHosts.get(i), i, pw);
+            }
+
+            N = mDeletedProviders.size();
+            pw.println(" ");
+            pw.println("Deleted Providers:");
+            for (int i=0; i<N; i++) {
+                dumpProvider(mDeletedProviders.get(i), i, pw);
+            }
+
+            N = mDeletedHosts.size();
+            pw.println(" ");
+            pw.println("Deleted Hosts:");
+            for (int i=0; i<N; i++) {
+                dumpHost(mDeletedHosts.get(i), i, pw);
+            }
+        }
+    }
+
+    private void ensureStateLoadedLocked() {
+        if (!mStateLoaded) {
+            loadAppWidgetList();
+            loadStateLocked();
+            mStateLoaded = true;
+        }
+    }
+
+    public int allocateAppWidgetId(String packageName, int hostId) {
+        int callingUid = enforceCallingUid(packageName);
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            int appWidgetId = mNextAppWidgetId++;
+
+            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
+
+            AppWidgetId id = new AppWidgetId();
+            id.appWidgetId = appWidgetId;
+            id.host = host;
+
+            host.instances.add(id);
+            mAppWidgetIds.add(id);
+
+            saveStateLocked();
+
+            return appWidgetId;
+        }
+    }
+
+    public void deleteAppWidgetId(int appWidgetId) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+            if (id != null) {
+                deleteAppWidgetLocked(id);
+                saveStateLocked();
+            }
+        }
+    }
+
+    public void deleteHost(int hostId) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            int callingUid = Binder.getCallingUid();
+            Host host = lookupHostLocked(callingUid, hostId);
+            if (host != null) {
+                deleteHostLocked(host);
+                saveStateLocked();
+            }
+        }
+    }
+
+    public void deleteAllHosts() {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            int callingUid = Binder.getCallingUid();
+            final int N = mHosts.size();
+            boolean changed = false;
+            for (int i = N - 1; i >= 0; i--) {
+                Host host = mHosts.get(i);
+                if (host.uid == callingUid) {
+                    deleteHostLocked(host);
+                    changed = true;
+                }
+            }
+            if (changed) {
+                saveStateLocked();
+            }
+        }
+    }
+
+    void deleteHostLocked(Host host) {
+        final int N = host.instances.size();
+        for (int i = N - 1; i >= 0; i--) {
+            AppWidgetId id = host.instances.get(i);
+            deleteAppWidgetLocked(id);
+        }
+        host.instances.clear();
+        mHosts.remove(host);
+        mDeletedHosts.add(host);
+        // it's gone or going away, abruptly drop the callback connection
+        host.callbacks = null;
+    }
+
+    void deleteAppWidgetLocked(AppWidgetId id) {
+        // We first unbind all services that are bound to this id
+        unbindAppWidgetRemoteViewsServicesLocked(id);
+
+        Host host = id.host;
+        host.instances.remove(id);
+        pruneHostLocked(host);
+
+        mAppWidgetIds.remove(id);
+
+        Provider p = id.provider;
+        if (p != null) {
+            p.instances.remove(id);
+            if (!p.zombie) {
+                // send the broacast saying that this appWidgetId has been deleted
+                Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
+                intent.setComponent(p.info.provider);
+                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
+                mContext.sendBroadcast(intent);
+                if (p.instances.size() == 0) {
+                    // cancel the future updates
+                    cancelBroadcasts(p);
+
+                    // send the broacast saying that the provider is not in use any more
+                    intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
+                    intent.setComponent(p.info.provider);
+                    mContext.sendBroadcast(intent);
+                }
+            }
+        }
+    }
+
+    void cancelBroadcasts(Provider p) {
+        if (p.broadcast != null) {
+            mAlarmManager.cancel(p.broadcast);
+            long token = Binder.clearCallingIdentity();
+            try {
+                p.broadcast.cancel();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            p.broadcast = null;
+        }
+    }
+
+    public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
+                "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mAppWidgetIds) {
+                ensureStateLoadedLocked();
+                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+                if (id == null) {
+                    throw new IllegalArgumentException("bad appWidgetId");
+                }
+                if (id.provider != null) {
+                    throw new IllegalArgumentException("appWidgetId " + appWidgetId
+                            + " already bound to " + id.provider.info.provider);
+                }
+                Provider p = lookupProviderLocked(provider);
+                if (p == null) {
+                    throw new IllegalArgumentException("not a appwidget provider: " + provider);
+                }
+                if (p.zombie) {
+                    throw new IllegalArgumentException("can't bind to a 3rd party provider in"
+                            + " safe mode: " + provider);
+                }
+
+                Binder.restoreCallingIdentity(ident);
+
+                id.provider = p;
+                p.instances.add(id);
+                int instancesSize = p.instances.size();
+                if (instancesSize == 1) {
+                    // tell the provider that it's ready
+                    sendEnableIntentLocked(p);
+                }
+
+                // send an update now -- We need this update now, and just for this appWidgetId.
+                // It's less critical when the next one happens, so when we schedule the next one,
+                // we add updatePeriodMillis to its start time. That time will have some slop,
+                // but that's okay.
+                sendUpdateIntentLocked(p, new int[] { appWidgetId });
+
+                // schedule the future updates
+                registerForBroadcastsLocked(p, getAppWidgetIds(p));
+                saveStateLocked();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    // Binds to a specific RemoteViewsService
+    public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+            if (id == null) {
+                throw new IllegalArgumentException("bad appWidgetId");
+            }
+            final ComponentName componentName = intent.getComponent();
+            try {
+                final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
+                        PackageManager.GET_PERMISSIONS);
+                if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
+                    throw new SecurityException("Selected service does not require "
+                            + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                throw new IllegalArgumentException("Unknown component " + componentName);
+            }
+
+            // If there is already a connection made for this service intent, then disconnect from
+            // that first. (This does not allow multiple connections to the same service under
+            // the same key)
+            ServiceConnectionProxy conn = null;
+            FilterComparison fc = new FilterComparison(intent);
+            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
+            if (mBoundRemoteViewsServices.containsKey(key)) {
+                conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
+                conn.disconnect();
+                mContext.unbindService(conn);
+                mBoundRemoteViewsServices.remove(key);
+            }
+
+            // Bind to the RemoteViewsService (which will trigger a callback to the
+            // RemoteViewsAdapter.onServiceConnected())
+            final long token = Binder.clearCallingIdentity();
+            try {
+                conn = new ServiceConnectionProxy(key, connection);
+                mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+                mBoundRemoteViewsServices.put(key, conn);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+
+            // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
+            // when we can call back to the RemoteViewsService later to destroy associated
+            // factories.
+            incrementAppWidgetServiceRefCount(appWidgetId, fc);
+        }
+    }
+
+    // Unbinds from a specific RemoteViewsService
+    public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            // Unbind from the RemoteViewsService (which will trigger a callback to the bound
+            // RemoteViewsAdapter)
+            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
+                    intent));
+            if (mBoundRemoteViewsServices.containsKey(key)) {
+                // We don't need to use the appWidgetId until after we are sure there is something
+                // to unbind. Note that this may mask certain issues with apps calling unbind()
+                // more than necessary.
+                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+                if (id == null) {
+                    throw new IllegalArgumentException("bad appWidgetId");
+                }
+
+                ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
+                        .get(key);
+                conn.disconnect();
+                mContext.unbindService(conn);
+                mBoundRemoteViewsServices.remove(key);
+            } else {
+                Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
+            }
+        }
+    }
+
+    // Unbinds from a RemoteViewsService when we delete an app widget
+    private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
+        int appWidgetId = id.appWidgetId;
+        // Unbind all connections to Services bound to this AppWidgetId
+        Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
+                .iterator();
+        while (it.hasNext()) {
+            final Pair<Integer, Intent.FilterComparison> key = it.next();
+            if (key.first.intValue() == appWidgetId) {
+                final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
+                        .get(key);
+                conn.disconnect();
+                mContext.unbindService(conn);
+                it.remove();
+            }
+        }
+
+        // Check if we need to destroy any services (if no other app widgets are
+        // referencing the same service)
+        decrementAppWidgetServiceRefCount(appWidgetId);
+    }
+
+    // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
+    private void destroyRemoteViewsService(final Intent intent) {
+        final ServiceConnection conn = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
+                try {
+                    cb.onDestroy(intent);
+                } catch (RemoteException e) {
+                    e.printStackTrace();
+                } catch (RuntimeException e) {
+                    e.printStackTrace();
+                }
+                mContext.unbindService(this);
+            }
+
+            @Override
+            public void onServiceDisconnected(android.content.ComponentName name) {
+                // Do nothing
+            }
+        };
+
+        // Bind to the service and remove the static intent->factory mapping in the
+        // RemoteViewsService.
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    // Adds to the ref-count for a given RemoteViewsService intent
+    private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
+        HashSet<Integer> appWidgetIds = null;
+        if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
+            appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
+        } else {
+            appWidgetIds = new HashSet<Integer>();
+            mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
+        }
+        appWidgetIds.add(appWidgetId);
+    }
+
+    // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
+    // the ref-count reaches zero.
+    private void decrementAppWidgetServiceRefCount(int appWidgetId) {
+        Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
+        while (it.hasNext()) {
+            final FilterComparison key = it.next();
+            final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
+            if (ids.remove(appWidgetId)) {
+                // If we have removed the last app widget referencing this service, then we
+                // should destroy it and remove it from this set
+                if (ids.isEmpty()) {
+                    destroyRemoteViewsService(key.getIntent());
+                    it.remove();
+                }
+            }
+        }
+    }
+
+    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+            if (id != null && id.provider != null && !id.provider.zombie) {
+                return id.provider.info;
+            }
+            return null;
+        }
+    }
+
+    public RemoteViews getAppWidgetViews(int appWidgetId) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+            if (id != null) {
+                return id.views;
+            }
+            return null;
+        }
+    }
+
+    public List<AppWidgetProviderInfo> getInstalledProviders() {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            final int N = mInstalledProviders.size();
+            ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
+            for (int i = 0; i < N; i++) {
+                Provider p = mInstalledProviders.get(i);
+                if (!p.zombie) {
+                    result.add(p.info);
+                }
+            }
+            return result;
+        }
+    }
+
+    public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
+        if (appWidgetIds == null) {
+            return;
+        }
+        if (appWidgetIds.length == 0) {
+            return;
+        }
+        final int N = appWidgetIds.length;
+
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            for (int i = 0; i < N; i++) {
+                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+                updateAppWidgetInstanceLocked(id, views);
+            }
+        }
+    }
+
+    public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
+        if (appWidgetIds == null) {
+            return;
+        }
+        if (appWidgetIds.length == 0) {
+            return;
+        }
+        final int N = appWidgetIds.length;
+
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            for (int i = 0; i < N; i++) {
+                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+                updateAppWidgetInstanceLocked(id, views, true);
+            }
+        }
+    }
+
+    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
+        if (appWidgetIds == null) {
+            return;
+        }
+        if (appWidgetIds.length == 0) {
+            return;
+        }
+        final int N = appWidgetIds.length;
+
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            for (int i = 0; i < N; i++) {
+                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+                notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
+            }
+        }
+    }
+
+    public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            Provider p = lookupProviderLocked(provider);
+            if (p == null) {
+                Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
+                return;
+            }
+            ArrayList<AppWidgetId> instances = p.instances;
+            final int callingUid = Binder.getCallingUid();
+            final int N = instances.size();
+            for (int i = 0; i < N; i++) {
+                AppWidgetId id = instances.get(i);
+                if (canAccessAppWidgetId(id, callingUid)) {
+                    updateAppWidgetInstanceLocked(id, views);
+                }
+            }
+        }
+    }
+
+    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
+        updateAppWidgetInstanceLocked(id, views, false);
+    }
+
+    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
+        // allow for stale appWidgetIds and other badness
+        // lookup also checks that the calling process can access the appWidgetId
+        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
+        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
+
+            // We do not want to save this RemoteViews
+            if (!isPartialUpdate)
+                id.views = views;
+
+            // is anyone listening?
+            if (id.host.callbacks != null) {
+                try {
+                    // the lock is held, but this is a oneway call
+                    id.host.callbacks.updateAppWidget(id.appWidgetId, views);
+                } catch (RemoteException e) {
+                    // It failed; remove the callback. No need to prune because
+                    // we know that this host is still referenced by this instance.
+                    id.host.callbacks = null;
+                }
+            }
+        }
+    }
+
+    void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
+        // allow for stale appWidgetIds and other badness
+        // lookup also checks that the calling process can access the appWidgetId
+        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
+        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
+            // is anyone listening?
+            if (id.host.callbacks != null) {
+                try {
+                    // the lock is held, but this is a oneway call
+                    id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
+                } catch (RemoteException e) {
+                    // It failed; remove the callback. No need to prune because
+                    // we know that this host is still referenced by this instance.
+                    id.host.callbacks = null;
+                }
+            }
+
+            // If the host is unavailable, then we call the associated
+            // RemoteViewsFactory.onDataSetChanged() directly
+            if (id.host.callbacks == null) {
+                Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
+                for (FilterComparison key : keys) {
+                    if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
+                        Intent intent = key.getIntent();
+
+                        final ServiceConnection conn = new ServiceConnection() {
+                            @Override
+                            public void onServiceConnected(ComponentName name, IBinder service) {
+                                IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
+                                        .asInterface(service);
+                                try {
+                                    cb.onDataSetChangedAsync();
+                                } catch (RemoteException e) {
+                                    e.printStackTrace();
+                                } catch (RuntimeException e) {
+                                    e.printStackTrace();
+                                }
+                                mContext.unbindService(this);
+                            }
+
+                            @Override
+                            public void onServiceDisconnected(android.content.ComponentName name) {
+                                // Do nothing
+                            }
+                        };
+
+                        // Bind to the service and call onDataSetChanged()
+                        final long token = Binder.clearCallingIdentity();
+                        try {
+                            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
+            List<RemoteViews> updatedViews) {
+        int callingUid = enforceCallingUid(packageName);
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
+            host.callbacks = callbacks;
+
+            updatedViews.clear();
+
+            ArrayList<AppWidgetId> instances = host.instances;
+            int N = instances.size();
+            int[] updatedIds = new int[N];
+            for (int i = 0; i < N; i++) {
+                AppWidgetId id = instances.get(i);
+                updatedIds[i] = id.appWidgetId;
+                updatedViews.add(id.views);
+            }
+            return updatedIds;
+        }
+    }
+
+    public void stopListening(int hostId) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
+            if (host != null) {
+                host.callbacks = null;
+                pruneHostLocked(host);
+            }
+        }
+    }
+
+    boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
+        if (id.host.uid == callingUid) {
+            // Apps hosting the AppWidget have access to it.
+            return true;
+        }
+        if (id.provider != null && id.provider.uid == callingUid) {
+            // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
+            return true;
+        }
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
+            // Apps that can bind have access to all appWidgetIds.
+            return true;
+        }
+        // Nobody else can access it.
+        return false;
+    }
+
+    AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
+        int callingUid = Binder.getCallingUid();
+        final int N = mAppWidgetIds.size();
+        for (int i = 0; i < N; i++) {
+            AppWidgetId id = mAppWidgetIds.get(i);
+            if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
+                return id;
+            }
+        }
+        return null;
+    }
+
+    Provider lookupProviderLocked(ComponentName provider) {
+        final int N = mInstalledProviders.size();
+        for (int i = 0; i < N; i++) {
+            Provider p = mInstalledProviders.get(i);
+            if (p.info.provider.equals(provider)) {
+                return p;
+            }
+        }
+        return null;
+    }
+
+    Host lookupHostLocked(int uid, int hostId) {
+        final int N = mHosts.size();
+        for (int i = 0; i < N; i++) {
+            Host h = mHosts.get(i);
+            if (h.uid == uid && h.hostId == hostId) {
+                return h;
+            }
+        }
+        return null;
+    }
+
+    Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
+        final int N = mHosts.size();
+        for (int i = 0; i < N; i++) {
+            Host h = mHosts.get(i);
+            if (h.hostId == hostId && h.packageName.equals(packageName)) {
+                return h;
+            }
+        }
+        Host host = new Host();
+        host.packageName = packageName;
+        host.uid = uid;
+        host.hostId = hostId;
+        mHosts.add(host);
+        return host;
+    }
+
+    void pruneHostLocked(Host host) {
+        if (host.instances.size() == 0 && host.callbacks == null) {
+            mHosts.remove(host);
+        }
+    }
+
+    void loadAppWidgetList() {
+        PackageManager pm = mPackageManager;
+
+        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+        List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
+                PackageManager.GET_META_DATA);
+
+        final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
+        for (int i = 0; i < N; i++) {
+            ResolveInfo ri = broadcastReceivers.get(i);
+            addProviderLocked(ri);
+        }
+    }
+
+    boolean addProviderLocked(ResolveInfo ri) {
+        if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+            return false;
+        }
+        if (!ri.activityInfo.isEnabled()) {
+            return false;
+        }
+        Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
+                ri.activityInfo.name), ri);
+        if (p != null) {
+            mInstalledProviders.add(p);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    void removeProviderLocked(int index, Provider p) {
+        int N = p.instances.size();
+        for (int i = 0; i < N; i++) {
+            AppWidgetId id = p.instances.get(i);
+            // Call back with empty RemoteViews
+            updateAppWidgetInstanceLocked(id, null);
+            // Stop telling the host about updates for this from now on
+            cancelBroadcasts(p);
+            // clear out references to this appWidgetId
+            id.host.instances.remove(id);
+            mAppWidgetIds.remove(id);
+            id.provider = null;
+            pruneHostLocked(id.host);
+            id.host = null;
+        }
+        p.instances.clear();
+        mInstalledProviders.remove(index);
+        mDeletedProviders.add(p);
+        // no need to send the DISABLE broadcast, since the receiver is gone anyway
+        cancelBroadcasts(p);
+    }
+
+    void sendEnableIntentLocked(Provider p) {
+        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
+        intent.setComponent(p.info.provider);
+        mContext.sendBroadcast(intent);
+    }
+
+    void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
+        if (appWidgetIds != null && appWidgetIds.length > 0) {
+            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
+            intent.setComponent(p.info.provider);
+            mContext.sendBroadcast(intent);
+        }
+    }
+
+    void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
+        if (p.info.updatePeriodMillis > 0) {
+            // if this is the first instance, set the alarm. otherwise,
+            // rely on the fact that we've already set it and that
+            // PendingIntent.getBroadcast will update the extras.
+            boolean alreadyRegistered = p.broadcast != null;
+            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
+            intent.setComponent(p.info.provider);
+            long token = Binder.clearCallingIdentity();
+            try {
+                p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
+                        PendingIntent.FLAG_UPDATE_CURRENT);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            if (!alreadyRegistered) {
+                long period = p.info.updatePeriodMillis;
+                if (period < MIN_UPDATE_PERIOD) {
+                    period = MIN_UPDATE_PERIOD;
+                }
+                mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
+                        .elapsedRealtime()
+                        + period, period, p.broadcast);
+            }
+        }
+    }
+
+    static int[] getAppWidgetIds(Provider p) {
+        int instancesSize = p.instances.size();
+        int appWidgetIds[] = new int[instancesSize];
+        for (int i = 0; i < instancesSize; i++) {
+            appWidgetIds[i] = p.instances.get(i).appWidgetId;
+        }
+        return appWidgetIds;
+    }
+
+    public int[] getAppWidgetIds(ComponentName provider) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            Provider p = lookupProviderLocked(provider);
+            if (p != null && Binder.getCallingUid() == p.uid) {
+                return getAppWidgetIds(p);
+            } else {
+                return new int[0];
+            }
+        }
+    }
+
+    private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
+        Provider p = null;
+
+        ActivityInfo activityInfo = ri.activityInfo;
+        XmlResourceParser parser = null;
+        try {
+            parser = activityInfo.loadXmlMetaData(mPackageManager,
+                    AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
+            if (parser == null) {
+                Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
+                        + " meta-data for " + "AppWidget provider '" + component + '\'');
+                return null;
+            }
+
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+                // drain whitespace, comments, etc.
+            }
+
+            String nodeName = parser.getName();
+            if (!"appwidget-provider".equals(nodeName)) {
+                Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
+                        + " AppWidget provider '" + component + '\'');
+                return null;
+            }
+
+            p = new Provider();
+            AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
+            info.provider = component;
+            p.uid = activityInfo.applicationInfo.uid;
+
+            Resources res = mPackageManager
+                    .getResourcesForApplication(activityInfo.applicationInfo);
+
+            TypedArray sa = res.obtainAttributes(attrs,
+                    com.android.internal.R.styleable.AppWidgetProviderInfo);
+
+            // These dimensions has to be resolved in the application's context.
+            // We simply send back the raw complex data, which will be
+            // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
+            TypedValue value = sa
+                    .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
+            info.minWidth = value != null ? value.data : 0;
+            value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
+            info.minHeight = value != null ? value.data : 0;
+            value = sa.peekValue(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
+            info.minResizeWidth = value != null ? value.data : info.minWidth;
+            value = sa.peekValue(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
+            info.minResizeHeight = value != null ? value.data : info.minHeight;
+            info.updatePeriodMillis = sa.getInt(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
+            info.initialLayout = sa.getResourceId(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
+            String className = sa
+                    .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
+            if (className != null) {
+                info.configure = new ComponentName(component.getPackageName(), className);
+            }
+            info.label = activityInfo.loadLabel(mPackageManager).toString();
+            info.icon = ri.getIconResource();
+            info.previewImage = sa.getResourceId(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
+            info.autoAdvanceViewId = sa.getResourceId(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
+            info.resizeMode = sa.getInt(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
+                    AppWidgetProviderInfo.RESIZE_NONE);
+
+            sa.recycle();
+        } catch (Exception e) {
+            // Ok to catch Exception here, because anything going wrong because
+            // of what a client process passes to us should not be fatal for the
+            // system process.
+            Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
+            return null;
+        } finally {
+            if (parser != null)
+                parser.close();
+        }
+        return p;
+    }
+
+    int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
+        PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
+        if (pkgInfo == null || pkgInfo.applicationInfo == null) {
+            throw new PackageManager.NameNotFoundException();
+        }
+        return pkgInfo.applicationInfo.uid;
+    }
+
+    int enforceCallingUid(String packageName) throws IllegalArgumentException {
+        int callingUid = Binder.getCallingUid();
+        int packageUid;
+        try {
+            packageUid = getUidForPackage(packageName);
+        } catch (PackageManager.NameNotFoundException ex) {
+            throw new IllegalArgumentException("packageName and uid don't match packageName="
+                    + packageName);
+        }
+        if (!UserId.isSameApp(callingUid, packageUid)) {
+            throw new IllegalArgumentException("packageName and uid don't match packageName="
+                    + packageName);
+        }
+        return callingUid;
+    }
+
+    void sendInitialBroadcasts() {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            final int N = mInstalledProviders.size();
+            for (int i = 0; i < N; i++) {
+                Provider p = mInstalledProviders.get(i);
+                if (p.instances.size() > 0) {
+                    sendEnableIntentLocked(p);
+                    int[] appWidgetIds = getAppWidgetIds(p);
+                    sendUpdateIntentLocked(p, appWidgetIds);
+                    registerForBroadcastsLocked(p, appWidgetIds);
+                }
+            }
+        }
+    }
+
+    // only call from initialization -- it assumes that the data structures are all empty
+    void loadStateLocked() {
+        AtomicFile file = savedStateFile();
+        try {
+            FileInputStream stream = file.openRead();
+            readStateFromFileLocked(stream);
+
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                    Slog.w(TAG, "Failed to close state FileInputStream " + e);
+                }
+            }
+        } catch (FileNotFoundException e) {
+            Slog.w(TAG, "Failed to read state: " + e);
+        }
+    }
+
+    void saveStateLocked() {
+        AtomicFile file = savedStateFile();
+        FileOutputStream stream;
+        try {
+            stream = file.startWrite();
+            if (writeStateToFileLocked(stream)) {
+                file.finishWrite(stream);
+            } else {
+                file.failWrite(stream);
+                Slog.w(TAG, "Failed to save state, restoring backup.");
+            }
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed open state file for write: " + e);
+        }
+    }
+
+    boolean writeStateToFileLocked(FileOutputStream stream) {
+        int N;
+
+        try {
+            XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(stream, "utf-8");
+            out.startDocument(null, true);
+            out.startTag(null, "gs");
+
+            int providerIndex = 0;
+            N = mInstalledProviders.size();
+            for (int i = 0; i < N; i++) {
+                Provider p = mInstalledProviders.get(i);
+                if (p.instances.size() > 0) {
+                    out.startTag(null, "p");
+                    out.attribute(null, "pkg", p.info.provider.getPackageName());
+                    out.attribute(null, "cl", p.info.provider.getClassName());
+                    out.endTag(null, "p");
+                    p.tag = providerIndex;
+                    providerIndex++;
+                }
+            }
+
+            N = mHosts.size();
+            for (int i = 0; i < N; i++) {
+                Host host = mHosts.get(i);
+                out.startTag(null, "h");
+                out.attribute(null, "pkg", host.packageName);
+                out.attribute(null, "id", Integer.toHexString(host.hostId));
+                out.endTag(null, "h");
+                host.tag = i;
+            }
+
+            N = mAppWidgetIds.size();
+            for (int i = 0; i < N; i++) {
+                AppWidgetId id = mAppWidgetIds.get(i);
+                out.startTag(null, "g");
+                out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
+                out.attribute(null, "h", Integer.toHexString(id.host.tag));
+                if (id.provider != null) {
+                    out.attribute(null, "p", Integer.toHexString(id.provider.tag));
+                }
+                out.endTag(null, "g");
+            }
+
+            out.endTag(null, "gs");
+
+            out.endDocument();
+            return true;
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed to write state: " + e);
+            return false;
+        }
+    }
+
+    void readStateFromFileLocked(FileInputStream stream) {
+        boolean success = false;
+
+        try {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(stream, null);
+
+            int type;
+            int providerIndex = 0;
+            HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
+            do {
+                type = parser.next();
+                if (type == XmlPullParser.START_TAG) {
+                    String tag = parser.getName();
+                    if ("p".equals(tag)) {
+                        // TODO: do we need to check that this package has the same signature
+                        // as before?
+                        String pkg = parser.getAttributeValue(null, "pkg");
+                        String cl = parser.getAttributeValue(null, "cl");
+
+                        final PackageManager packageManager = mContext.getPackageManager();
+                        try {
+                            packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
+                        } catch (PackageManager.NameNotFoundException e) {
+                            String[] pkgs = packageManager
+                                    .currentToCanonicalPackageNames(new String[] { pkg });
+                            pkg = pkgs[0];
+                        }
+
+                        Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
+                        if (p == null && mSafeMode) {
+                            // if we're in safe mode, make a temporary one
+                            p = new Provider();
+                            p.info = new AppWidgetProviderInfo();
+                            p.info.provider = new ComponentName(pkg, cl);
+                            p.zombie = true;
+                            mInstalledProviders.add(p);
+                        }
+                        if (p != null) {
+                            // if it wasn't uninstalled or something
+                            loadedProviders.put(providerIndex, p);
+                        }
+                        providerIndex++;
+                    } else if ("h".equals(tag)) {
+                        Host host = new Host();
+
+                        // TODO: do we need to check that this package has the same signature
+                        // as before?
+                        host.packageName = parser.getAttributeValue(null, "pkg");
+                        try {
+                            host.uid = getUidForPackage(host.packageName);
+                        } catch (PackageManager.NameNotFoundException ex) {
+                            host.zombie = true;
+                        }
+                        if (!host.zombie || mSafeMode) {
+                            // In safe mode, we don't discard the hosts we don't recognize
+                            // so that they're not pruned from our list. Otherwise, we do.
+                            host.hostId = Integer
+                                    .parseInt(parser.getAttributeValue(null, "id"), 16);
+                            mHosts.add(host);
+                        }
+                    } else if ("g".equals(tag)) {
+                        AppWidgetId id = new AppWidgetId();
+                        id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
+                        if (id.appWidgetId >= mNextAppWidgetId) {
+                            mNextAppWidgetId = id.appWidgetId + 1;
+                        }
+
+                        String providerString = parser.getAttributeValue(null, "p");
+                        if (providerString != null) {
+                            // there's no provider if it hasn't been bound yet.
+                            // maybe we don't have to save this, but it brings the system
+                            // to the state it was in.
+                            int pIndex = Integer.parseInt(providerString, 16);
+                            id.provider = loadedProviders.get(pIndex);
+                            if (false) {
+                                Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
+                                        + pIndex + " which is " + id.provider);
+                            }
+                            if (id.provider == null) {
+                                // This provider is gone. We just let the host figure out
+                                // that this happened when it fails to load it.
+                                continue;
+                            }
+                        }
+
+                        int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
+                        id.host = mHosts.get(hIndex);
+                        if (id.host == null) {
+                            // This host is gone.
+                            continue;
+                        }
+
+                        if (id.provider != null) {
+                            id.provider.instances.add(id);
+                        }
+                        id.host.instances.add(id);
+                        mAppWidgetIds.add(id);
+                    }
+                }
+            } while (type != XmlPullParser.END_DOCUMENT);
+            success = true;
+        } catch (NullPointerException e) {
+            Slog.w(TAG, "failed parsing " + e);
+        } catch (NumberFormatException e) {
+            Slog.w(TAG, "failed parsing " + e);
+        } catch (XmlPullParserException e) {
+            Slog.w(TAG, "failed parsing " + e);
+        } catch (IOException e) {
+            Slog.w(TAG, "failed parsing " + e);
+        } catch (IndexOutOfBoundsException e) {
+            Slog.w(TAG, "failed parsing " + e);
+        }
+
+        if (success) {
+            // delete any hosts that didn't manage to get connected (should happen)
+            // if it matters, they'll be reconnected.
+            for (int i = mHosts.size() - 1; i >= 0; i--) {
+                pruneHostLocked(mHosts.get(i));
+            }
+        } else {
+            // failed reading, clean up
+            Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
+
+            mAppWidgetIds.clear();
+            mHosts.clear();
+            final int N = mInstalledProviders.size();
+            for (int i = 0; i < N; i++) {
+                mInstalledProviders.get(i).instances.clear();
+            }
+        }
+    }
+
+    AtomicFile savedStateFile() {
+        int userId = Binder.getOrigCallingUser();
+        File dir = new File("/data/system/users/" + userId);
+        File settingsFile = new File(dir, SETTINGS_FILENAME);
+        if (!dir.exists()) {
+            dir.mkdirs();
+            if (userId == 0) {
+                // Migrate old data
+                File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
+                // Method doesn't throw an exception on failure. Ignore any errors
+                // in moving the file (like non-existence)
+                oldFile.renameTo(settingsFile);
+            }
+        }
+        return new AtomicFile(settingsFile);
+    }
+
+    void addProvidersForPackageLocked(String pkgName) {
+        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+        intent.setPackage(pkgName);
+        List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
+                PackageManager.GET_META_DATA);
+
+        final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
+        for (int i = 0; i < N; i++) {
+            ResolveInfo ri = broadcastReceivers.get(i);
+            ActivityInfo ai = ri.activityInfo;
+            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+                continue;
+            }
+            if (pkgName.equals(ai.packageName)) {
+                addProviderLocked(ri);
+            }
+        }
+    }
+
+    void updateProvidersForPackageLocked(String pkgName) {
+        HashSet<String> keep = new HashSet<String>();
+        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+        intent.setPackage(pkgName);
+        List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
+                PackageManager.GET_META_DATA);
+
+        // add the missing ones and collect which ones to keep
+        int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
+        for (int i = 0; i < N; i++) {
+            ResolveInfo ri = broadcastReceivers.get(i);
+            ActivityInfo ai = ri.activityInfo;
+            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+                continue;
+            }
+            if (pkgName.equals(ai.packageName)) {
+                ComponentName component = new ComponentName(ai.packageName, ai.name);
+                Provider p = lookupProviderLocked(component);
+                if (p == null) {
+                    if (addProviderLocked(ri)) {
+                        keep.add(ai.name);
+                    }
+                } else {
+                    Provider parsed = parseProviderInfoXml(component, ri);
+                    if (parsed != null) {
+                        keep.add(ai.name);
+                        // Use the new AppWidgetProviderInfo.
+                        p.info = parsed.info;
+                        // If it's enabled
+                        final int M = p.instances.size();
+                        if (M > 0) {
+                            int[] appWidgetIds = getAppWidgetIds(p);
+                            // Reschedule for the new updatePeriodMillis (don't worry about handling
+                            // it specially if updatePeriodMillis didn't change because we just sent
+                            // an update, and the next one will be updatePeriodMillis from now).
+                            cancelBroadcasts(p);
+                            registerForBroadcastsLocked(p, appWidgetIds);
+                            // If it's currently showing, call back with the new
+                            // AppWidgetProviderInfo.
+                            for (int j = 0; j < M; j++) {
+                                AppWidgetId id = p.instances.get(j);
+                                id.views = null;
+                                if (id.host != null && id.host.callbacks != null) {
+                                    try {
+                                        id.host.callbacks.providerChanged(id.appWidgetId, p.info);
+                                    } catch (RemoteException ex) {
+                                        // It failed; remove the callback. No need to prune because
+                                        // we know that this host is still referenced by this
+                                        // instance.
+                                        id.host.callbacks = null;
+                                    }
+                                }
+                            }
+                            // Now that we've told the host, push out an update.
+                            sendUpdateIntentLocked(p, appWidgetIds);
+                        }
+                    }
+                }
+            }
+        }
+
+        // prune the ones we don't want to keep
+        N = mInstalledProviders.size();
+        for (int i = N - 1; i >= 0; i--) {
+            Provider p = mInstalledProviders.get(i);
+            if (pkgName.equals(p.info.provider.getPackageName())
+                    && !keep.contains(p.info.provider.getClassName())) {
+                removeProviderLocked(i, p);
+            }
+        }
+    }
+
+    void removeProvidersForPackageLocked(String pkgName) {
+        int N = mInstalledProviders.size();
+        for (int i = N - 1; i >= 0; i--) {
+            Provider p = mInstalledProviders.get(i);
+            if (pkgName.equals(p.info.provider.getPackageName())) {
+                removeProviderLocked(i, p);
+            }
+        }
+
+        // Delete the hosts for this package too
+        //
+        // By now, we have removed any AppWidgets that were in any hosts here,
+        // so we don't need to worry about sending DISABLE broadcasts to them.
+        N = mHosts.size();
+        for (int i = N - 1; i >= 0; i--) {
+            Host host = mHosts.get(i);
+            if (pkgName.equals(host.packageName)) {
+                deleteHostLocked(host);
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 31515e1..a7b08f5 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -1675,7 +1675,8 @@
         synchronized(mClearDataLock) {
             mClearingData = true;
             try {
-                mActivityManager.clearApplicationUserData(packageName, observer);
+                mActivityManager.clearApplicationUserData(packageName, observer,
+                        Binder.getOrigCallingUser());
             } catch (RemoteException e) {
                 // can't happen because the activity manager is in this process
             }
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index f475dd6..308661f 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -153,6 +153,10 @@
                         start = i + 1;
                     }
                 }
+                if (start == 0) {
+                    final String rawEvent = new String(buffer, start, count, Charsets.UTF_8);
+                    log("RCV incomplete <- {" + rawEvent + "}");
+                }
 
                 // We should end at the amount we read. If not, compact then
                 // buffer and read again.
@@ -297,7 +301,11 @@
             throws NativeDaemonConnectorException {
         final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();
 
-        mResponseQueue.clear();
+        while (mResponseQueue.size() > 0) {
+            try {
+                log("ignoring {" + mResponseQueue.take() + "}");
+            } catch (Exception e) {}
+        }
 
         final String sentCommand = sendCommandLocked(cmd, args);
 
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 5039294..34a8a02 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -45,6 +45,7 @@
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserId;
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
@@ -1034,7 +1035,7 @@
         try {
             ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(
                     pkg, 0);
-            if (ai.uid != uid) {
+            if (!UserId.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/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 3a6a3de..e6a1e68 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -75,6 +75,7 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -104,6 +105,7 @@
 import android.os.StrictMode;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserId;
 import android.provider.Settings;
 import android.util.EventLog;
 import android.util.Pair;
@@ -111,6 +113,7 @@
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 import android.util.TimeUtils;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -135,6 +138,7 @@
 import java.lang.IllegalStateException;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -149,7 +153,9 @@
 
 public final class ActivityManagerService extends ActivityManagerNative
         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
+    private static final String USER_DATA_DIR = "/data/user/";
     static final String TAG = "ActivityManager";
+    static final String TAG_MU = "ActivityManagerServiceMU";
     static final boolean DEBUG = false;
     static final boolean localLOGV = DEBUG;
     static final boolean DEBUG_SWITCH = localLOGV || false;
@@ -172,6 +178,7 @@
     static final boolean DEBUG_CONFIGURATION = localLOGV || false;
     static final boolean DEBUG_POWER = localLOGV || false;
     static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
+    static final boolean DEBUG_MU = localLOGV || false;
     static final boolean VALIDATE_TOKENS = false;
     static final boolean SHOW_ACTIVITY_START_TIME = true;
     
@@ -781,7 +788,16 @@
                 r.curComponent = new ComponentName(
                         info.activityInfo.applicationInfo.packageName,
                         info.activityInfo.name);
+                if (r.callingUid != Process.SYSTEM_UID) {
+                    info.activityInfo = getActivityInfoForUser(info.activityInfo, UserId
+                            .getUserId(r.callingUid));
+                }
                 r.curReceiver = info.activityInfo;
+                if (DEBUG_MU && r.callingUid > UserId.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);
+                }
 
                 // Broadcast is being executed, its package can't be stopped.
                 try {
@@ -1286,17 +1302,7 @@
     final HashMap<String, ArrayList<Intent>> mStickyBroadcasts =
             new HashMap<String, ArrayList<Intent>>();
 
-    /**
-     * All currently running services.
-     */
-    final HashMap<ComponentName, ServiceRecord> mServices =
-        new HashMap<ComponentName, ServiceRecord>();
-
-    /**
-     * All currently running services indexed by the Intent used to start them.
-     */
-    final HashMap<Intent.FilterComparison, ServiceRecord> mServicesByIntent =
-        new HashMap<Intent.FilterComparison, ServiceRecord>();
+    final ServiceMap mServiceMap = new ServiceMap();
 
     /**
      * All currently bound service connections.  Keys are the IBinder of
@@ -1362,6 +1368,8 @@
     final HashMap<ComponentName, ContentProviderRecord> mProvidersByClass
             = new HashMap<ComponentName, ContentProviderRecord>();
 
+    final ProviderMap mProviderMap = new ProviderMap();
+
     /**
      * List of content providers who have clients waiting for them.  The
      * application is currently being launched and the provider will be
@@ -1392,6 +1400,7 @@
             uid = _uid;
         }
     }
+
     private static ThreadLocal<Identity> sCallerIdentity = new ThreadLocal<Identity>();
 
     /**
@@ -1688,7 +1697,7 @@
                     }
                     broadcastIntentLocked(null, null, intent,
                             null, null, 0, null, null, null,
-                            false, false, MY_PID, Process.SYSTEM_UID);
+                            false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
 
                     Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
                             mContext, proc, (ActivityRecord)data.get("activity"));
@@ -2587,6 +2596,7 @@
                     processName);
             return procs != null ? procs.valueAt(0) : null;
         }
+        // uid = applyUserId(uid);
         ProcessRecord proc = mProcessNames.get(processName, uid);
         return proc;
     }
@@ -2716,6 +2726,7 @@
         
         try {
             int uid = app.info.uid;
+
             int[] gids = null;
             try {
                 gids = mContext.getPackageManager().getPackageGids(
@@ -2827,7 +2838,7 @@
         }
     }
 
-    boolean startHomeActivityLocked() {
+    boolean startHomeActivityLocked(int userId) {
         if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
                 && mTopAction == null) {
             // We are running in factory test mode, but unable to find
@@ -2850,6 +2861,8 @@
                     aInfo.applicationInfo.packageName, aInfo.name));
             // Don't do this if the home app is currently being
             // instrumented.
+            aInfo = new ActivityInfo(aInfo);
+            aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
             ProcessRecord app = getProcessRecordLocked(aInfo.processName,
                     aInfo.applicationInfo.uid);
             if (app == null || app.instrumentationClass == null) {
@@ -2858,11 +2871,10 @@
                         null, null, 0, 0, 0, false, false, null);
             }
         }
-        
-        
+
         return true;
     }
-    
+
     /**
      * Starts the "new version setup screen" if appropriate.
      */
@@ -3026,10 +3038,23 @@
             int grantedMode, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug,
             String profileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
+        int userId = 0;
+        if (intent.getCategories() != null && intent.getCategories().contains(Intent.CATEGORY_HOME)) {
+            // Requesting home, set the identity to the current user
+            // HACK!
+            userId = mCurrentUserId;
+        } else {
+            // TODO: Fix this in a better way - calls coming from SystemUI should probably carry
+            // the current user's userId
+            if (Binder.getCallingUid() < Process.FIRST_APPLICATION_UID) {
+                userId = 0;
+            } else {
+                userId = Binder.getOrigCallingUser();
+            }
+        }
         return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
-                grantedUriPermissions, grantedMode, resultTo, resultWho,
-                requestCode, onlyIfNeeded, debug, profileFile, profileFd, autoStopProfiler,
-                null, null);
+                grantedUriPermissions, grantedMode, resultTo, resultWho, requestCode, onlyIfNeeded,
+                debug, profileFile, profileFd, autoStopProfiler, null, null, userId);
     }
 
     public final WaitResult startActivityAndWait(IApplicationThread caller,
@@ -3038,10 +3063,11 @@
             String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug,
             String profileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
         WaitResult res = new WaitResult();
+        int userId = Binder.getOrigCallingUser();
         mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
                 grantedUriPermissions, grantedMode, resultTo, resultWho,
                 requestCode, onlyIfNeeded, debug, profileFile, profileFd, autoStopProfiler,
-                res, null);
+                res, null, userId);
         return res;
     }
     
@@ -3050,9 +3076,11 @@
             int grantedMode, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded,
             boolean debug, Configuration config) {
-        return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
+        int ret = mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
                 grantedUriPermissions, grantedMode, resultTo, resultWho,
-                requestCode, onlyIfNeeded, debug, null, null, false, null, config);
+                requestCode, onlyIfNeeded,
+                debug, null, null, false, null, config, Binder.getOrigCallingUser());
+        return ret;
     }
 
     public int startActivityIntentSender(IApplicationThread caller,
@@ -3080,9 +3108,9 @@
                 mAppSwitchesAllowedTime = 0;
             }
         }
-        
-        return pir.sendInner(0, fillInIntent, resolvedType, null,
+        int ret = pir.sendInner(0, fillInIntent, resolvedType, null,
                 null, resultTo, resultWho, requestCode, flagsMask, flagsValues);
+        return ret;
     }
     
     public boolean startNextMatchingActivity(IBinder callingActivity,
@@ -3185,20 +3213,24 @@
         
         // This is so super not safe, that only the system (or okay root)
         // can do it.
+        int userId = Binder.getOrigCallingUser();
         final int callingUid = Binder.getCallingUid();
         if (callingUid != 0 && callingUid != Process.myUid()) {
             throw new SecurityException(
                     "startActivityInPackage only available to the system");
         }
 
-        return mMainStack.startActivityMayWait(null, uid, intent, resolvedType,
+        int ret = mMainStack.startActivityMayWait(null, uid, intent, resolvedType,
                 null, 0, resultTo, resultWho, requestCode, onlyIfNeeded, false,
-                null, null, false, null, null);
+                null, null, false, null, null, userId);
+        return ret;
     }
 
     public final int startActivities(IApplicationThread caller,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
-        return mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo);
+        int ret = mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo,
+                Binder.getOrigCallingUser());
+        return ret;
     }
 
     public final int startActivitiesInPackage(int uid,
@@ -3211,8 +3243,9 @@
             throw new SecurityException(
                     "startActivityInPackage only available to the system");
         }
-
-        return mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo);
+        int ret = mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo,
+                UserId.getUserId(uid));
+        return ret;
     }
 
     final void addRecentTaskLocked(TaskRecord task) {
@@ -3224,8 +3257,9 @@
         // Remove any existing entries that are the same kind of task.
         for (int i=0; i<N; i++) {
             TaskRecord tr = mRecentTasks.get(i);
-            if ((task.affinity != null && task.affinity.equals(tr.affinity))
-                    || (task.intent != null && task.intent.filterEquals(tr.intent))) {
+            if (task.userId == tr.userId
+                    && ((task.affinity != null && task.affinity.equals(tr.affinity))
+                    || (task.intent != null && task.intent.filterEquals(tr.intent)))) {
                 mRecentTasks.remove(i);
                 i--;
                 N--;
@@ -3584,7 +3618,6 @@
 
     private final int getLRURecordIndexForAppLocked(IApplicationThread thread) {
         IBinder threadBinder = thread.asBinder();
-
         // Find the application record.
         for (int i=mLruProcesses.size()-1; i>=0; i--) {
             ProcessRecord rec = mLruProcesses.get(i);
@@ -3961,7 +3994,7 @@
     }
     
     public boolean clearApplicationUserData(final String packageName,
-            final IPackageDataObserver observer) {
+            final IPackageDataObserver observer, final int userId) {
         int uid = Binder.getCallingUid();
         int pid = Binder.getCallingPid();
         long callingId = Binder.clearCallingIdentity();
@@ -3996,7 +4029,7 @@
                         Uri.fromParts("package", packageName, null));
                 intent.putExtra(Intent.EXTRA_UID, pkgUid);
                 broadcastIntentInPackage("android", Process.SYSTEM_UID, intent,
-                        null, null, 0, null, null, null, false, false);
+                        null, null, 0, null, null, null, false, false, userId);
             } catch (RemoteException e) {
             }
         } finally {
@@ -4091,7 +4124,7 @@
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
-        
+        final int userId = Binder.getOrigCallingUser();
         long callingId = Binder.clearCallingIdentity();
         try {
             IPackageManager pm = AppGlobals.getPackageManager();
@@ -4099,6 +4132,8 @@
             synchronized(this) {
                 try {
                     pkgUid = pm.getPackageUid(packageName);
+                    // Convert the uid to the one for the calling user
+                    pkgUid = UserId.getUid(userId, pkgUid);
                 } catch (RemoteException e) {
                 }
                 if (pkgUid == -1) {
@@ -4181,7 +4216,7 @@
             }
             
             broadcastIntentLocked(null, null, intent, null,
-                    null, 0, null, null, null, false, false, -1, uid);
+                    null, 0, null, null, null, false, false, -1, uid, 0 /* TODO: Verify */);
         }
         Binder.restoreCallingIdentity(origId);
     }
@@ -4241,7 +4276,8 @@
         intent.putExtra(Intent.EXTRA_UID, uid);
         broadcastIntentLocked(null, null, intent,
                 null, null, 0, null, null, null,
-                false, false, MY_PID, Process.SYSTEM_UID);
+                false, false,
+                MY_PID, Process.SYSTEM_UID, UserId.getUserId(uid));
     }
     
     private final boolean killPackageProcessesLocked(String packageName, int uid,
@@ -4345,7 +4381,8 @@
         }
 
         ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
-        for (ServiceRecord service : mServices.values()) {
+        int userId = UserId.getUserId(uid);
+        for (ServiceRecord service : mServiceMap.getAllServices(userId)) {
             if (service.packageName.equals(name)
                     && (service.app == null || evenPersistent || !service.app.persistent)) {
                 if (!doit) {
@@ -4809,11 +4846,12 @@
                 // Tell anyone interested that we are done booting!
                 SystemProperties.set("sys.boot_completed", "1");
                 SystemProperties.set("dev.bootcomplete", "1");
+                /* TODO: Send this to all users that are to be logged in on startup */
                 broadcastIntentLocked(null, null,
                         new Intent(Intent.ACTION_BOOT_COMPLETED, null),
                         null, null, 0, null, null,
                         android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
-                        false, false, MY_PID, Process.SYSTEM_UID);
+                        false, false, MY_PID, Process.SYSTEM_UID, Binder.getOrigCallingUser());
             }
         }
     }
@@ -4954,7 +4992,7 @@
                 if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
                     int uid = AppGlobals.getPackageManager()
                             .getPackageUid(packageName);
-                    if (uid != Binder.getCallingUid()) {
+                    if (UserId.getAppId(callingUid) != uid) {
                         String msg = "Permission Denial: getIntentSender() from pid="
                             + Binder.getCallingPid()
                             + ", uid=" + Binder.getCallingUid()
@@ -4965,7 +5003,10 @@
                     }
                 }
                 
-                return getIntentSenderLocked(type, packageName, callingUid,
+                if (DEBUG_MU)
+                    Slog.i(TAG_MU, "Getting intent sender for origCallingUid="
+                            + Binder.getOrigCallingUid());
+                return getIntentSenderLocked(type, packageName, Binder.getOrigCallingUid(),
                         token, resultWho, requestCode, intents, resolvedTypes, flags);
                 
             } catch (RemoteException e) {
@@ -4977,6 +5018,8 @@
     IIntentSender getIntentSenderLocked(int type,
             String packageName, int callingUid, IBinder token, String resultWho,
             int requestCode, Intent[] intents, String[] resolvedTypes, int flags) {
+        if (DEBUG_MU)
+            Slog.v(TAG_MU, "getIntentSenderLocked(): uid=" + callingUid);
         ActivityRecord activity = null;
         if (type == INTENT_SENDER_ACTIVITY_RESULT) {
             activity = mMainStack.isInStackLocked(token);
@@ -5220,7 +5263,7 @@
         }
         // 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 && uid == owningUid) {
+        if (owningUid >= 0 && UserId.isSameApp(uid, owningUid)) {
             return PackageManager.PERMISSION_GRANTED;
         }
         // If the target is not exported, then nobody else can get to it.
@@ -5254,7 +5297,7 @@
         if (permission == null) {
             return PackageManager.PERMISSION_DENIED;
         }
-        return checkComponentPermission(permission, pid, uid, -1, true);
+        return checkComponentPermission(permission, pid, UserId.getAppId(uid), -1, true);
     }
 
     /**
@@ -5264,7 +5307,7 @@
     int checkCallingPermission(String permission) {
         return checkPermission(permission,
                 Binder.getCallingPid(),
-                Binder.getCallingUid());
+                UserId.getAppId(Binder.getCallingUid()));
     }
 
     /**
@@ -5378,6 +5421,7 @@
             pid = tlsIdentity.pid;
         }
 
+        uid = UserId.getAppId(uid);
         // Our own process gets to do everything.
         if (pid == MY_PID) {
             return PackageManager.PERMISSION_GRANTED;
@@ -5420,7 +5464,8 @@
 
         String name = uri.getAuthority();
         ProviderInfo pi = null;
-        ContentProviderRecord cpr = mProvidersByName.get(name);
+        ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
+                UserId.getUserId(callingUid));
         if (cpr != null) {
             pi = cpr.info;
         } else {
@@ -5676,7 +5721,8 @@
 
         final String authority = uri.getAuthority();
         ProviderInfo pi = null;
-        ContentProviderRecord cpr = mProvidersByName.get(authority);
+        ContentProviderRecord cpr = mProviderMap.getProviderByName(authority,
+                UserId.getUserId(callingUid));
         if (cpr != null) {
             pi = cpr.info;
         } else {
@@ -5770,7 +5816,8 @@
 
             final String authority = uri.getAuthority();
             ProviderInfo pi = null;
-            ContentProviderRecord cpr = mProvidersByName.get(authority);
+            ContentProviderRecord cpr = mProviderMap.getProviderByName(authority,
+                    UserId.getUserId(r.info.uid));
             if (cpr != null) {
                 pi = cpr.info;
             } else {
@@ -6009,6 +6056,12 @@
 
     public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
             int flags) {
+        final int callingUid = Binder.getCallingUid();
+        // If it's the system uid asking, then use the current user id.
+        // TODO: Make sure that there aren't any other legitimate calls from the system uid that
+        // require the entire list.
+        final int callingUserId = callingUid == Process.SYSTEM_UID
+                ? mCurrentUserId : UserId.getUserId(callingUid);
         synchronized (this) {
             enforceCallingPermission(android.Manifest.permission.GET_TASKS,
                     "getRecentTasks()");
@@ -6021,11 +6074,14 @@
                             maxNum < N ? maxNum : N);
             for (int i=0; i<N && maxNum > 0; i++) {
                 TaskRecord tr = mRecentTasks.get(i);
+                // Only add calling user's recent tasks
+                if (tr.userId != callingUserId) continue;
                 // Return the entry if desired by the caller.  We always return
                 // the first entry, because callers always expect this to be the
-                // forground app.  We may filter others if the caller has
+                // foreground app.  We may filter others if the caller has
                 // not supplied RECENT_WITH_EXCLUDED and there is some reason
                 // we should exclude the entry.
+
                 if (i == 0
                         || ((flags&ActivityManager.RECENT_WITH_EXCLUDED) != 0)
                         || (tr.intent == null)
@@ -6114,7 +6170,7 @@
 
         // Find any running services associated with this app.
         ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
-        for (ServiceRecord sr : mServices.values()) {
+        for (ServiceRecord sr : mServiceMap.getAllServices(root.userId)) {
             if (sr.packageName.equals(component.getPackageName())) {
                 services.add(sr);
             }
@@ -6485,17 +6541,23 @@
                         STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
         } catch (RemoteException ex) {
         }
+        if (DEBUG_MU)
+            Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.info.uid);
+        int userId = UserId.getUserId(app.info.uid);
         if (providers != null) {
             final int N = providers.size();
             for (int i=0; i<N; i++) {
                 ProviderInfo cpi =
                     (ProviderInfo)providers.get(i);
+
                 ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
-                ContentProviderRecord cpr = mProvidersByClass.get(comp);
+                ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
                 if (cpr == null) {
                     cpr = new ContentProviderRecord(cpi, app.info, comp);
-                    mProvidersByClass.put(comp, cpr);
+                    mProviderMap.putProviderByClass(comp, cpr);
                 }
+                if (DEBUG_MU)
+                    Slog.v(TAG_MU, "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid);
                 app.pubProviders.put(cpi.name, cpr);
                 app.addPackage(cpi.applicationInfo.packageName);
                 ensurePackageDexOpt(cpi.applicationInfo.packageName);
@@ -6605,8 +6667,8 @@
         return false;
     }
 
-    private final ContentProviderHolder getContentProviderImpl(
-        IApplicationThread caller, String name) {
+    private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
+            String name) {
         ContentProviderRecord cpr;
         ProviderInfo cpi = null;
 
@@ -6623,7 +6685,8 @@
             }
 
             // First check if this content provider has been published...
-            cpr = mProvidersByName.get(name);
+            int userId = UserId.getUserId(r != null ? r.info.uid : Binder.getCallingUid());
+            cpr = mProviderMap.getProviderByName(name, userId);
             boolean providerRunning = cpr != null;
             if (providerRunning) {
                 cpi = cpr.info;
@@ -6708,6 +6771,9 @@
                     return null;
                 }
 
+                cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo,
+                        Binder.getOrigCallingUser());
+
                 String msg;
                 if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
                     throw new SecurityException(msg);
@@ -6723,7 +6789,7 @@
                 }
 
                 ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
-                cpr = mProvidersByClass.get(comp);
+                cpr = mProviderMap.getProviderByClass(comp, Binder.getOrigCallingUser());
                 final boolean firstClass = cpr == null;
                 if (firstClass) {
                     try {
@@ -6737,6 +6803,7 @@
                                     + cpi.name);
                             return null;
                         }
+                        ai = getAppInfoForUser(ai, Binder.getOrigCallingUser());
                         cpr = new ContentProviderRecord(cpi, ai, comp);
                     } catch (RemoteException ex) {
                         // pm is in same process, this will never happen.
@@ -6805,9 +6872,9 @@
                 // Make sure the provider is published (the same provider class
                 // may be published under multiple names).
                 if (firstClass) {
-                    mProvidersByClass.put(comp, cpr);
+                    mProviderMap.putProviderByClass(comp, cpr);
                 }
-                mProvidersByName.put(name, cpr);
+                mProviderMap.putProviderByName(name, cpr);
                 incProviderCount(r, cpr);
             }
         }
@@ -6826,6 +6893,10 @@
                     return null;
                 }
                 try {
+                    if (DEBUG_MU) {
+                        Slog.v(TAG_MU, "Waiting to start provider " + cpr + " launchingApp="
+                                + cpr.launchingApp);
+                    }
                     cpr.wait();
                 } catch (InterruptedException ex) {
                 }
@@ -6843,7 +6914,8 @@
             throw new SecurityException(msg);
         }
 
-        return getContentProviderImpl(caller, name);
+        ContentProviderHolder contentProvider = getContentProviderImpl(caller, name);
+        return contentProvider;
     }
 
     private ContentProviderHolder getContentProviderExternal(String name) {
@@ -6856,7 +6928,8 @@
      */
     public void removeContentProvider(IApplicationThread caller, String name) {
         synchronized (this) {
-            ContentProviderRecord cpr = mProvidersByName.get(name);
+            int userId = UserId.getUserId(Binder.getCallingUid());
+            ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId);
             if(cpr == null) {
                 // remove from mProvidersByClass
                 if (DEBUG_PROVIDER) Slog.v(TAG, name +
@@ -6871,8 +6944,11 @@
             }
             //update content provider record entry info
             ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
-            ContentProviderRecord localCpr = mProvidersByClass.get(comp);
-            if (localCpr.proc == r) {
+            ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
+            if (DEBUG_PROVIDER) Slog.v(TAG, "Removing provider requested by "
+                    + r.info.processName + " from process "
+                    + localCpr.appInfo.processName);
+            if (localCpr.launchingApp == r) {
                 //should not happen. taken care of as a local provider
                 Slog.w(TAG, "removeContentProvider called on local provider: "
                         + cpr.info.name + " in process " + r.processName);
@@ -6887,7 +6963,8 @@
 
     private void removeContentProviderExternal(String name) {
         synchronized (this) {
-            ContentProviderRecord cpr = mProvidersByName.get(name);
+            ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
+                    Binder.getOrigCallingUser());
             if(cpr == null) {
                 //remove from mProvidersByClass
                 if(localLOGV) Slog.v(TAG, name+" content provider not found in providers list");
@@ -6896,7 +6973,8 @@
 
             //update content provider record entry info
             ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
-            ContentProviderRecord localCpr = mProvidersByClass.get(comp);
+            ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp,
+                    Binder.getOrigCallingUser());
             localCpr.externals--;
             if (localCpr.externals < 0) {
                 Slog.e(TAG, "Externals < 0 for content provider " + localCpr);
@@ -6913,6 +6991,8 @@
 
         synchronized(this) {
             final ProcessRecord r = getRecordForAppLocked(caller);
+            if (DEBUG_MU)
+                Slog.v(TAG_MU, "ProcessRecord uid = " + r.info.uid);
             if (r == null) {
                 throw new SecurityException(
                         "Unable to find app for caller " + caller
@@ -6929,12 +7009,14 @@
                     continue;
                 }
                 ContentProviderRecord dst = r.pubProviders.get(src.info.name);
+                if (DEBUG_MU)
+                    Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
                 if (dst != null) {
                     ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
-                    mProvidersByClass.put(comp, dst);
+                    mProviderMap.putProviderByClass(comp, dst);
                     String names[] = dst.info.authority.split(";");
                     for (int j = 0; j < names.length; j++) {
-                        mProvidersByName.put(names[j], dst);
+                        mProviderMap.putProviderByName(names[j], dst);
                     }
 
                     int NL = mLaunchingProviders.size();
@@ -7679,8 +7761,10 @@
                             };
                         }
                         Slog.i(TAG, "Sending system update to: " + intent.getComponent());
+                        /* TODO: Send this to all users */
                         broadcastIntentLocked(null, null, intent, null, finisher,
-                                0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID);
+                                0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID,
+                                Process.SYSTEM_UID);
                         if (finisher != null) {
                             mWaitingUpdate = true;
                         }
@@ -9306,8 +9390,14 @@
 
         if ("all".equals(name)) {
             synchronized (this) {
-                for (ServiceRecord r1 : mServices.values()) {
-                    services.add(r1);
+                try {
+                    List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+                    for (UserInfo user : users) {
+                        for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) {
+                            services.add(r1);
+                        }
+                    }
+                } catch (RemoteException re) {
                 }
             }
         } else {
@@ -9325,18 +9415,24 @@
             }
 
             synchronized (this) {
-                for (ServiceRecord r1 : mServices.values()) {
-                    if (componentName != null) {
-                        if (r1.name.equals(componentName)) {
-                            services.add(r1);
+                try {
+                    List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+                    for (UserInfo user : users) {
+                        for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) {
+                            if (componentName != null) {
+                                if (r1.name.equals(componentName)) {
+                                    services.add(r1);
+                                }
+                            } else if (name != null) {
+                                if (r1.name.flattenToString().contains(name)) {
+                                    services.add(r1);
+                                }
+                            } else if (System.identityHashCode(r1) == objectId) {
+                                services.add(r1);
+                            }
                         }
-                    } else if (name != null) {
-                        if (r1.name.flattenToString().contains(name)) {
-                            services.add(r1);
-                        }
-                    } else if (System.identityHashCode(r1) == objectId) {
-                        services.add(r1);
                     }
+                } catch (RemoteException re) {
                 }
             }
         }
@@ -9778,75 +9874,87 @@
         matcher.build(args, opti);
 
         pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)");
-        if (mServices.size() > 0) {
-            boolean printed = false;
-            long nowReal = SystemClock.elapsedRealtime();
-            Iterator<ServiceRecord> it = mServices.values().iterator();
-            needSep = false;
-            while (it.hasNext()) {
-                ServiceRecord r = it.next();
-                if (!matcher.match(r, r.name)) {
-                    continue;
-                }
-                if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
-                    continue;
-                }
-                if (!printed) {
-                    pw.println("  Active services:");
-                    printed = true;
-                }
-                if (needSep) {
-                    pw.println();
-                }
-                pw.print("  * "); pw.println(r);
-                if (dumpAll) {
-                    r.dump(pw, "    ");
-                    needSep = true;
-                } else {
-                    pw.print("    app="); pw.println(r.app);
-                    pw.print("    created=");
-                        TimeUtils.formatDuration(r.createTime, nowReal, pw);
-                        pw.print(" started="); pw.print(r.startRequested);
-                        pw.print(" connections="); pw.println(r.connections.size());
-                    if (r.connections.size() > 0) {
-                        pw.println("    Connections:");
-                        for (ArrayList<ConnectionRecord> clist : r.connections.values()) {
-                            for (int i=0; i<clist.size(); i++) {
-                                ConnectionRecord conn = clist.get(i);
-                                pw.print("      ");
-                                pw.print(conn.binding.intent.intent.getIntent().toShortString(
-                                        false, false, false));
-                                pw.print(" -> ");
-                                ProcessRecord proc = conn.binding.client;
-                                pw.println(proc != null ? proc.toShortString() : "null");
+        try {
+            List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+            for (UserInfo user : users) {
+                if (mServiceMap.getAllServices(user.id).size() > 0) {
+                    boolean printed = false;
+                    long nowReal = SystemClock.elapsedRealtime();
+                    Iterator<ServiceRecord> it = mServiceMap.getAllServices(
+                            user.id).iterator();
+                    needSep = false;
+                    while (it.hasNext()) {
+                        ServiceRecord r = it.next();
+                        if (!matcher.match(r, r.name)) {
+                            continue;
+                        }
+                        if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
+                            continue;
+                        }
+                        if (!printed) {
+                            pw.println("  Active services:");
+                            printed = true;
+                        }
+                        if (needSep) {
+                            pw.println();
+                        }
+                        pw.print("  * ");
+                        pw.println(r);
+                        if (dumpAll) {
+                            r.dump(pw, "    ");
+                            needSep = true;
+                        } else {
+                            pw.print("    app=");
+                            pw.println(r.app);
+                            pw.print("    created=");
+                            TimeUtils.formatDuration(r.createTime, nowReal, pw);
+                            pw.print(" started=");
+                            pw.print(r.startRequested);
+                            pw.print(" connections=");
+                            pw.println(r.connections.size());
+                            if (r.connections.size() > 0) {
+                                pw.println("    Connections:");
+                                for (ArrayList<ConnectionRecord> clist : r.connections.values()) {
+                                    for (int i = 0; i < clist.size(); i++) {
+                                        ConnectionRecord conn = clist.get(i);
+                                        pw.print("      ");
+                                        pw.print(conn.binding.intent.intent.getIntent()
+                                                .toShortString(false, false, false));
+                                        pw.print(" -> ");
+                                        ProcessRecord proc = conn.binding.client;
+                                        pw.println(proc != null ? proc.toShortString() : "null");
+                                    }
+                                }
                             }
                         }
-                    }
-                }
-                if (dumpClient && r.app != null && r.app.thread != null) {
-                    pw.println("    Client:");
-                    pw.flush();
-                    try {
-                        TransferPipe tp = new TransferPipe();
-                        try {
-                            r.app.thread.dumpService(
-                                    tp.getWriteFd().getFileDescriptor(), r, args);
-                            tp.setBufferPrefix("      ");
-                            // Short timeout, since blocking here can
-                            // deadlock with the application.
-                            tp.go(fd, 2000);
-                        } finally {
-                            tp.kill();
+                        if (dumpClient && r.app != null && r.app.thread != null) {
+                            pw.println("    Client:");
+                            pw.flush();
+                            try {
+                                TransferPipe tp = new TransferPipe();
+                                try {
+                                    r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(),
+                                            r, args);
+                                    tp.setBufferPrefix("      ");
+                                    // Short timeout, since blocking here can
+                                    // deadlock with the application.
+                                    tp.go(fd, 2000);
+                                } finally {
+                                    tp.kill();
+                                }
+                            } catch (IOException e) {
+                                pw.println("      Failure while dumping the service: " + e);
+                            } catch (RemoteException e) {
+                                pw.println("      Got a RemoteException while dumping the service");
+                            }
+                            needSep = true;
                         }
-                    } catch (IOException e) {
-                        pw.println("      Failure while dumping the service: " + e);
-                    } catch (RemoteException e) {
-                        pw.println("      Got a RemoteException while dumping the service");
                     }
-                    needSep = true;
+                    needSep = printed;
                 }
             }
-            needSep = printed;
+        } catch (RemoteException re) {
+
         }
 
         if (mPendingServices.size() > 0) {
@@ -9956,76 +10064,8 @@
         matcher.build(args, opti);
 
         pw.println("ACTIVITY MANAGER CONTENT PROVIDERS (dumpsys activity providers)");
-        if (mProvidersByClass.size() > 0) {
-            boolean printed = false;
-            Iterator<Map.Entry<ComponentName, ContentProviderRecord>> it
-                    = mProvidersByClass.entrySet().iterator();
-            while (it.hasNext()) {
-                Map.Entry<ComponentName, ContentProviderRecord> e = it.next();
-                ContentProviderRecord r = e.getValue();
-                ComponentName comp = e.getKey();
-                String cls = comp.getClassName();
-                int end = cls.lastIndexOf('.');
-                if (end > 0 && end < (cls.length()-2)) {
-                    cls = cls.substring(end+1);
-                }
-                if (!matcher.match(r, comp)) {
-                    continue;
-                }
-                if (dumpPackage != null && !dumpPackage.equals(comp.getPackageName())) {
-                    continue;
-                }
-                if (!printed) {
-                    if (needSep) pw.println(" ");
-                    needSep = true;
-                    pw.println("  Published content providers (by class):");
-                    printed = true;
-                }
-                pw.print("  * "); pw.print(cls); pw.print(" (");
-                        pw.print(comp.flattenToShortString()); pw.println(")");
-                if (dumpAll) {
-                    r.dump(pw, "      ");
-                } else {
-                    if (r.proc != null) {
-                        pw.print("      "); pw.println(r.proc);
-                    } else {
-                        pw.println();
-                    }
-                    if (r.clients.size() > 0) {
-                        pw.println("      Clients:");
-                        for (ProcessRecord cproc : r.clients) {
-                            pw.print("        - "); pw.println(cproc);
-                        }
-                    }
-                }
-            }
-        }
-    
-        if (dumpAll) {
-            if (mProvidersByName.size() > 0) {
-                boolean printed = false;
-                Iterator<Map.Entry<String, ContentProviderRecord>> it
-                        = mProvidersByName.entrySet().iterator();
-                while (it.hasNext()) {
-                    Map.Entry<String, ContentProviderRecord> e = it.next();
-                    ContentProviderRecord r = e.getValue();
-                    if (!matcher.match(r, r.name)) {
-                        continue;
-                    }
-                    if (dumpPackage != null && !dumpPackage.equals(r.name.getPackageName())) {
-                        continue;
-                    }
-                    if (!printed) {
-                        if (needSep) pw.println(" ");
-                        needSep = true;
-                        pw.println("  Authority to provider mappings:");
-                        printed = true;
-                    }
-                    pw.print("  "); pw.print(e.getKey()); pw.println(":");
-                    pw.print("    "); pw.println(r);
-                }
-            }
-        }
+
+        mProviderMap.dumpProvidersLocked(pw, dumpAll);
 
         if (mLaunchingProviders.size() > 0) {
             boolean printed = false;
@@ -10911,10 +10951,10 @@
             cpr.notifyAll();
         }
         
-        mProvidersByClass.remove(cpr.name);
+        mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid));
         String names[] = cpr.info.authority.split(";");
         for (int j = 0; j < names.length; j++) {
-            mProvidersByName.remove(names[j]);
+            mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid));
         }
         
         Iterator<ProcessRecord> cit = cpr.clients.iterator();
@@ -11190,8 +11230,10 @@
             ArrayList<ActivityManager.RunningServiceInfo> res
                     = new ArrayList<ActivityManager.RunningServiceInfo>();
             
-            if (mServices.size() > 0) {
-                Iterator<ServiceRecord> it = mServices.values().iterator();
+            int userId = UserId.getUserId(Binder.getCallingUid());
+            if (mServiceMap.getAllServices(userId).size() > 0) {
+                Iterator<ServiceRecord> it
+                        = mServiceMap.getAllServices(userId).iterator();
                 while (it.hasNext() && res.size() < maxNum) {
                     res.add(makeRunningServiceInfoLocked(it.next()));
                 }
@@ -11211,7 +11253,8 @@
 
     public PendingIntent getRunningServiceControlPanel(ComponentName name) {
         synchronized (this) {
-            ServiceRecord r = mServices.get(name);
+            int userId = UserId.getUserId(Binder.getCallingUid());
+            ServiceRecord r = mServiceMap.getServiceByName(name, userId);
             if (r != null) {
                 for (ArrayList<ConnectionRecord> conn : r.connections.values()) {
                     for (int i=0; i<conn.size(); i++) {
@@ -11227,7 +11270,7 @@
     
     private final ServiceRecord findServiceLocked(ComponentName name,
             IBinder token) {
-        ServiceRecord r = mServices.get(name);
+        ServiceRecord r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
         return r == token ? r : null;
     }
 
@@ -11245,11 +11288,11 @@
             String resolvedType) {
         ServiceRecord r = null;
         if (service.getComponent() != null) {
-            r = mServices.get(service.getComponent());
+            r = mServiceMap.getServiceByName(service.getComponent(), Binder.getOrigCallingUser());
         }
         if (r == null) {
             Intent.FilterComparison filter = new Intent.FilterComparison(service);
-            r = mServicesByIntent.get(filter);
+            r = mServiceMap.getServiceByIntent(filter, Binder.getOrigCallingUser());
         }
 
         if (r == null) {
@@ -11265,7 +11308,7 @@
 
                 ComponentName name = new ComponentName(
                         sInfo.applicationInfo.packageName, sInfo.name);
-                r = mServices.get(name);
+                r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
             } catch (RemoteException ex) {
                 // pm is in same process, this will never happen.
             }
@@ -11312,11 +11355,17 @@
     private ServiceLookupResult retrieveServiceLocked(Intent service,
             String resolvedType, int callingPid, int callingUid) {
         ServiceRecord r = null;
+        if (DEBUG_SERVICE)
+            Slog.v(TAG, "retrieveServiceLocked: " + service + " type=" + resolvedType
+                    + " origCallingUid=" + callingUid);
+
         if (service.getComponent() != null) {
-            r = mServices.get(service.getComponent());
+            r = mServiceMap.getServiceByName(service.getComponent(), Binder.getOrigCallingUser());
         }
-        Intent.FilterComparison filter = new Intent.FilterComparison(service);
-        r = mServicesByIntent.get(filter);
+        if (r == null) {
+            Intent.FilterComparison filter = new Intent.FilterComparison(service);
+            r = mServiceMap.getServiceByIntent(filter, Binder.getOrigCallingUser());
+        }
         if (r == null) {
             try {
                 ResolveInfo rInfo =
@@ -11329,12 +11378,16 @@
                           ": not found");
                     return null;
                 }
-
+                if (Binder.getOrigCallingUser() > 0) {
+                    sInfo.applicationInfo = getAppInfoForUser(sInfo.applicationInfo,
+                            Binder.getOrigCallingUser());
+                }
                 ComponentName name = new ComponentName(
                         sInfo.applicationInfo.packageName, sInfo.name);
-                r = mServices.get(name);
+                r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
                 if (r == null) {
-                    filter = new Intent.FilterComparison(service.cloneFilter());
+                    Intent.FilterComparison filter = new Intent.FilterComparison(
+                            service.cloneFilter());
                     ServiceRestarter res = new ServiceRestarter();
                     BatteryStatsImpl.Uid.Pkg.Serv ss = null;
                     BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
@@ -11345,8 +11398,8 @@
                     }
                     r = new ServiceRecord(this, ss, name, filter, sInfo, res);
                     res.setService(r);
-                    mServices.put(name, r);
-                    mServicesByIntent.put(filter, r);
+                    mServiceMap.putServiceByName(name, UserId.getUserId(r.appInfo.uid), r);
+                    mServiceMap.putServiceByIntent(filter, UserId.getUserId(r.appInfo.uid), r);
                     
                     // Make sure this component isn't in the pending list.
                     int N = mPendingServices.size();
@@ -11493,7 +11546,9 @@
         if (app.thread == null) {
             throw new RemoteException();
         }
-
+        if (DEBUG_MU)
+            Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
+                    + ", ProcessRecord.uid = " + app.info.uid);
         r.app = app;
         r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
 
@@ -11690,6 +11745,8 @@
 
         final String appName = r.processName;
         ProcessRecord app = getProcessRecordLocked(appName, r.appInfo.uid);
+        if (DEBUG_MU)
+            Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app);
         if (app != null && app.thread != null) {
             try {
                 app.addPackage(r.appInfo.packageName);
@@ -11794,8 +11851,8 @@
                 System.identityHashCode(r), r.shortName,
                 (r.app != null) ? r.app.pid : -1);
 
-        mServices.remove(r.name);
-        mServicesByIntent.remove(r.intent);
+        mServiceMap.removeServiceByName(r.name, r.userId);
+        mServiceMap.removeServiceByIntent(r.intent, r.userId);
         r.totalRestartCount = 0;
         unscheduleServiceRestartLocked(r);
 
@@ -11909,6 +11966,8 @@
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
 
+        if (DEBUG_SERVICE)
+            Slog.v(TAG, "startService: " + service + " type=" + resolvedType);
         synchronized(this) {
             final int callingPid = Binder.getCallingPid();
             final int callingUid = Binder.getCallingUid();
@@ -11923,6 +11982,8 @@
     ComponentName startServiceInPackage(int uid,
             Intent service, String resolvedType) {
         synchronized(this) {
+            if (DEBUG_SERVICE)
+                Slog.v(TAG, "startServiceInPackage: " + service + " type=" + resolvedType);
             final long origId = Binder.clearCallingIdentity();
             ComponentName res = startServiceLocked(null, service,
                     resolvedType, -1, uid);
@@ -12126,6 +12187,9 @@
             if (DEBUG_SERVICE) Slog.v(TAG, "bindService: " + service
                     + " type=" + resolvedType + " conn=" + connection.asBinder()
                     + " flags=0x" + Integer.toHexString(flags));
+            if (DEBUG_MU)
+                Slog.i(TAG_MU, "bindService uid=" + Binder.getCallingUid() + " origUid="
+                        + Binder.getOrigCallingUid());
             final ProcessRecord callerApp = getRecordForAppLocked(caller);
             if (callerApp == null) {
                 throw new SecurityException(
@@ -12168,7 +12232,7 @@
             
             ServiceLookupResult res =
                 retrieveServiceLocked(service, resolvedType,
-                        Binder.getCallingPid(), Binder.getCallingUid());
+                        Binder.getCallingPid(), Binder.getOrigCallingUid());
             if (res == null) {
                 return 0;
             }
@@ -12514,7 +12578,9 @@
                         r.callStart = false;
                     }
                 }
-                
+                if (DEBUG_MU)
+                    Slog.v(TAG_MU, "before serviceDontExecutingLocked, uid="
+                            + Binder.getOrigCallingUid());
                 final long origId = Binder.clearCallingIdentity();
                 serviceDoneExecutingLocked(r, inStopping);
                 Binder.restoreCallingIdentity(origId);
@@ -12927,7 +12993,8 @@
             String callerPackage, Intent intent, String resolvedType,
             IIntentReceiver resultTo, int resultCode, String resultData,
             Bundle map, String requiredPermission,
-            boolean ordered, boolean sticky, int callingPid, int callingUid) {
+            boolean ordered, boolean sticky, int callingPid, int callingUid,
+            int userId) {
         intent = new Intent(intent);
 
         // By default broadcasts do not go to stopped apps.
@@ -13102,10 +13169,11 @@
                 if (ai != null) {
                     receivers = new ArrayList();
                     ResolveInfo ri = new ResolveInfo();
-                    ri.activityInfo = ai;
+                    ri.activityInfo = getActivityInfoForUser(ai, userId);
                     receivers.add(ri);
                 }
             } else {
+                // TODO: Apply userId
                 // Need to resolve the intent to interested receivers...
                 if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                          == 0) {
@@ -13274,7 +13342,7 @@
     public final int broadcastIntent(IApplicationThread caller,
             Intent intent, String resolvedType, IIntentReceiver resultTo,
             int resultCode, String resultData, Bundle map,
-            String requiredPermission, boolean serialized, boolean sticky) {
+            String requiredPermission, boolean serialized, boolean sticky, int userId) {
         synchronized(this) {
             intent = verifyBroadcastLocked(intent);
             
@@ -13285,8 +13353,8 @@
             int res = broadcastIntentLocked(callerApp,
                     callerApp != null ? callerApp.info.packageName : null,
                     intent, resolvedType, resultTo,
-                    resultCode, resultData, map, requiredPermission, serialized,
-                    sticky, callingPid, callingUid);
+                    resultCode, resultData, map, requiredPermission, serialized, sticky,
+                    callingPid, callingUid, userId);
             Binder.restoreCallingIdentity(origId);
             return res;
         }
@@ -13295,21 +13363,21 @@
     int broadcastIntentInPackage(String packageName, int uid,
             Intent intent, String resolvedType, IIntentReceiver resultTo,
             int resultCode, String resultData, Bundle map,
-            String requiredPermission, boolean serialized, boolean sticky) {
+            String requiredPermission, boolean serialized, boolean sticky, int userId) {
         synchronized(this) {
             intent = verifyBroadcastLocked(intent);
 
             final long origId = Binder.clearCallingIdentity();
             int res = broadcastIntentLocked(null, packageName, intent, resolvedType,
                     resultTo, resultCode, resultData, map, requiredPermission,
-                    serialized, sticky, -1, uid);
+                    serialized, sticky, -1, uid, userId);
             Binder.restoreCallingIdentity(origId);
             return res;
         }
     }
 
-    public final void unbroadcastIntent(IApplicationThread caller,
-            Intent intent) {
+    // TODO: Use the userId; maybe mStickyBroadcasts need to be tied to the user.
+    public final void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) {
         // Refuse possible leaked file descriptors
         if (intent != null && intent.hasFileDescriptors() == true) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -13514,7 +13582,6 @@
         }
     }
 
-
     // =========================================================
     // INSTRUMENTATION
     // =========================================================
@@ -13786,12 +13853,12 @@
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                         | Intent.FLAG_RECEIVER_REPLACE_PENDING);
                 broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
-                        null, false, false, MY_PID, Process.SYSTEM_UID);
+                        null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
                 if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
                     broadcastIntentLocked(null, null,
                             new Intent(Intent.ACTION_LOCALE_CHANGED),
                             null, null, 0, null, null,
-                            null, false, false, MY_PID, Process.SYSTEM_UID);
+                            null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
                 }
             }
         }
@@ -15134,8 +15201,158 @@
 
     // Multi-user methods
 
-    public boolean switchUser(int userid) {
-        // TODO
+    private int mCurrentUserId;
+    private SparseIntArray mLoggedInUsers = new SparseIntArray(5);
+
+    public boolean switchUser(int userId) {
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid != 0 && callingUid != Process.myUid()) {
+            Slog.e(TAG, "Trying to switch user from unauthorized app");
+            return false;
+        }
+        if (mCurrentUserId == userId)
+            return true;
+
+        synchronized (this) {
+            // Check if user is already logged in, otherwise check if user exists first before
+            // adding to the list of logged in users.
+            if (mLoggedInUsers.indexOfKey(userId) < 0) {
+                if (!userExists(userId)) {
+                    return false;
+                }
+                mLoggedInUsers.append(userId, userId);
+            }
+
+            mCurrentUserId = userId;
+            boolean haveActivities = mMainStack.switchUser(userId);
+            if (!haveActivities) {
+                startHomeActivityLocked(userId);
+            }
+        }
         return true;
     }
+
+    private boolean userExists(int userId) {
+        try {
+            List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+            for (UserInfo user : users) {
+                if (user.id == userId) {
+                    return true;
+                }
+            }
+        } catch (RemoteException re) {
+            // Won't happen, in same process
+        }
+
+        return false;
+    }
+
+
+    private int applyUserId(int uid, int userId) {
+        return UserId.getUid(userId, uid);
+    }
+
+    private ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) {
+        if (info == null) return null;
+        ApplicationInfo newInfo = new ApplicationInfo(info);
+        newInfo.uid = applyUserId(info.uid, userId);
+        if (newInfo.uid >= Process.FIRST_APPLICATION_UID) {
+            newInfo.dataDir = USER_DATA_DIR + userId + "/"
+                    + info.packageName;
+        }
+        return newInfo;
+    }
+
+    ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) {
+        if (aInfo == null || aInfo.applicationInfo.uid < Process.FIRST_APPLICATION_UID
+                || userId < 1) {
+            return aInfo;
+        }
+
+        ActivityInfo info = new ActivityInfo(aInfo);
+        info.applicationInfo = getAppInfoForUser(info.applicationInfo, userId);
+        return info;
+    }
+
+    static class ServiceMap {
+
+        private final SparseArray<HashMap<ComponentName, ServiceRecord>> mServicesByNamePerUser
+                = new SparseArray<HashMap<ComponentName, ServiceRecord>>();
+        private final SparseArray<HashMap<Intent.FilterComparison, ServiceRecord>>
+                mServicesByIntentPerUser = new SparseArray<
+                    HashMap<Intent.FilterComparison, ServiceRecord>>();
+
+        ServiceRecord getServiceByName(ComponentName name, int callingUser) {
+            // TODO: Deal with global services
+            if (DEBUG_MU)
+                Slog.v(TAG_MU, "getServiceByName(" + name + "), callingUser = " + callingUser);
+            return getServices(callingUser).get(name);
+        }
+
+        ServiceRecord getServiceByName(ComponentName name) {
+            return getServiceByName(name, -1);
+        }
+
+        ServiceRecord getServiceByIntent(Intent.FilterComparison filter, int callingUser) {
+            // TODO: Deal with global services
+            if (DEBUG_MU)
+                Slog.v(TAG_MU, "getServiceByIntent(" + filter + "), callingUser = " + callingUser);
+            return getServicesByIntent(callingUser).get(filter);
+        }
+
+        ServiceRecord getServiceByIntent(Intent.FilterComparison filter) {
+            return getServiceByIntent(filter, -1);
+        }
+
+        void putServiceByName(ComponentName name, int callingUser, ServiceRecord value) {
+            // TODO: Deal with global services
+            getServices(callingUser).put(name, value);
+        }
+
+        void putServiceByIntent(Intent.FilterComparison filter, int callingUser,
+                ServiceRecord value) {
+            // TODO: Deal with global services
+            getServicesByIntent(callingUser).put(filter, value);
+        }
+
+        void removeServiceByName(ComponentName name, int callingUser) {
+            // TODO: Deal with global services
+            ServiceRecord removed = getServices(callingUser).remove(name);
+            if (DEBUG_MU)
+                Slog.v(TAG, "removeServiceByName user=" + callingUser + " name=" + name
+                        + " removed=" + removed);
+        }
+
+        void removeServiceByIntent(Intent.FilterComparison filter, int callingUser) {
+            // TODO: Deal with global services
+            ServiceRecord removed = getServicesByIntent(callingUser).remove(filter);
+            if (DEBUG_MU)
+                Slog.v(TAG_MU, "removeServiceByIntent user=" + callingUser + " intent=" + filter
+                        + " removed=" + removed);
+        }
+
+        Collection<ServiceRecord> getAllServices(int callingUser) {
+            // TODO: Deal with global services
+            return getServices(callingUser).values();
+        }
+
+        private HashMap<ComponentName, ServiceRecord> getServices(int callingUser) {
+            HashMap map = mServicesByNamePerUser.get(callingUser);
+            if (map == null) {
+                map = new HashMap<ComponentName, ServiceRecord>();
+                mServicesByNamePerUser.put(callingUser, map);
+            }
+            return map;
+        }
+
+        private HashMap<Intent.FilterComparison, ServiceRecord> getServicesByIntent(
+                int callingUser) {
+            HashMap map = mServicesByIntentPerUser.get(callingUser);
+            if (map == null) {
+                map = new HashMap<Intent.FilterComparison, ServiceRecord>();
+                mServicesByIntentPerUser.put(callingUser, map);
+            }
+            return map;
+        }
+    }
 }
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index c819114..cdab6c6 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -25,6 +25,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.res.CompatibilityInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.os.Build;
@@ -34,6 +35,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserId;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
@@ -55,6 +57,7 @@
     final IApplicationToken.Stub appToken; // window manager token
     final ActivityInfo info; // all about me
     final int launchedFromUid; // always the uid who started the activity.
+    final int userId;          // Which user is this running for?
     final Intent intent;    // the original intent that generated us
     final ComponentName realActivity;  // the intent component, or target of an alias.
     final String shortComponentName; // the short component name of the intent
@@ -124,6 +127,7 @@
         pw.print(prefix); pw.print("packageName="); pw.print(packageName);
                 pw.print(" processName="); pw.println(processName);
         pw.print(prefix); pw.print("launchedFromUid="); pw.print(launchedFromUid);
+        pw.print(prefix); pw.print("userId="); pw.print(userId);
                 pw.print(" app="); pw.println(app);
         pw.print(prefix); pw.println(intent.toInsecureString());
         pw.print(prefix); pw.print("frontOfTask="); pw.print(frontOfTask);
@@ -281,6 +285,7 @@
         appToken = new Token(this);
         info = aInfo;
         launchedFromUid = _launchedFromUid;
+        userId = UserId.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 6c11953..c3ae6a1 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -60,6 +60,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserId;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
@@ -274,6 +275,8 @@
     int mThumbnailWidth = -1;
     int mThumbnailHeight = -1;
 
+    private int mCurrentUser;
+
     static final int SLEEP_TIMEOUT_MSG = 8;
     static final int PAUSE_TIMEOUT_MSG = 9;
     static final int IDLE_TIMEOUT_MSG = 10;
@@ -365,6 +368,7 @@
     }
     
     final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
+        // TODO: Don't look for any tasks from other users
         int i = mHistory.size()-1;
         while (i >= 0) {
             ActivityRecord r = mHistory.get(i);
@@ -377,6 +381,7 @@
     }
 
     final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
+        // TODO: Don't look for any tasks from other users
         int i = mHistory.size()-1;
         while (i >= 0) {
             ActivityRecord r = mHistory.get(i);
@@ -398,6 +403,7 @@
      * @return Returns the HistoryRecord of the next activity on the stack.
      */
     final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
+        // TODO: Don't look for any tasks from other users
         int i = mHistory.size()-1;
         while (i >= 0) {
             ActivityRecord r = mHistory.get(i);
@@ -444,10 +450,11 @@
 
         TaskRecord cp = null;
 
+        final int userId = UserId.getUserId(info.applicationInfo.uid);
         final int N = mHistory.size();
         for (int i=(N-1); i>=0; i--) {
             ActivityRecord r = mHistory.get(i);
-            if (!r.finishing && r.task != cp
+            if (!r.finishing && r.task != cp && r.userId == userId
                     && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
                 cp = r.task;
                 //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString()
@@ -487,12 +494,13 @@
         if (info.targetActivity != null) {
             cls = new ComponentName(info.packageName, info.targetActivity);
         }
+        final int userId = UserId.getUserId(info.applicationInfo.uid);
 
         final int N = mHistory.size();
         for (int i=(N-1); i>=0; i--) {
             ActivityRecord r = mHistory.get(i);
             if (!r.finishing) {
-                if (r.intent.getComponent().equals(cls)) {
+                if (r.intent.getComponent().equals(cls) && r.userId == userId) {
                     //Slog.i(TAG, "Found matching class!");
                     //dump();
                     //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
@@ -511,6 +519,43 @@
         mService.mHandler.sendMessage(msg);
     }
 
+    /*
+     * Move the activities around in the stack to bring a user to the foreground.
+     * @return whether there are any activities for the specified user.
+     */
+    final boolean switchUser(int userId) {
+        synchronized (mService) {
+            mCurrentUser = userId;
+
+            // Only one activity? Nothing to do...
+            if (mHistory.size() < 2)
+                return false;
+
+            boolean haveActivities = false;
+            // Check if the top activity is from the new user.
+            ActivityRecord top = mHistory.get(mHistory.size() - 1);
+            if (top.userId == userId) return true;
+            // Otherwise, move the user's activities to the top.
+            int N = mHistory.size();
+            int i = 0;
+            while (i < N) {
+                ActivityRecord r = mHistory.get(i);
+                if (r.userId == userId) {
+                    ActivityRecord moveToTop = mHistory.remove(i);
+                    mHistory.add(moveToTop);
+                    // No need to check the top one now
+                    N--;
+                    haveActivities = true;
+                } else {
+                    i++;
+                }
+            }
+            // Transition from the old top to the new top
+            resumeTopActivityLocked(top);
+            return haveActivities;
+        }
+    }
+
     final boolean realStartActivityLocked(ActivityRecord r,
             ProcessRecord app, boolean andResume, boolean checkConfig)
             throws RemoteException {
@@ -1272,7 +1317,7 @@
             // There are no more activities!  Let's just start up the
             // Launcher...
             if (mMainStack) {
-                return mService.startHomeActivityLocked();
+                return mService.startHomeActivityLocked(0);
             }
         }
 
@@ -1384,6 +1429,7 @@
         // Launching this app's activity, make sure the app is no longer
         // considered stopped.
         try {
+            // TODO: Apply to the correct userId
             AppGlobals.getPackageManager().setPackageStoppedState(
                     next.packageName, false);
         } catch (RemoteException e1) {
@@ -2354,7 +2400,7 @@
                 }
             }
         }
-        
+
         ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,
                 intent, resolvedType, aInfo, mService.mConfiguration,
                 resultRecord, resultWho, requestCode, componentSpecified);
@@ -2420,7 +2466,8 @@
             int grantedMode, boolean onlyIfNeeded, boolean doResume) {
         final Intent intent = r.intent;
         final int callingUid = r.launchedFromUid;
-        
+        final int userId = r.userId;
+
         int launchFlags = intent.getFlags();
         
         // We'll invoke onUserLeaving before onPause only if the launching
@@ -2648,7 +2695,7 @@
             // once.
             ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
             if (top != null && r.resultTo == null) {
-                if (top.realActivity.equals(r.realActivity)) {
+                if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
                     if (top.app != null && top.app.thread != null) {
                         if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
                             || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
@@ -2821,12 +2868,12 @@
             int grantedMode, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded,
             boolean debug, String profileFile, ParcelFileDescriptor profileFd,
-            boolean autoStopProfiler, WaitResult outResult, Configuration config) {
+            boolean autoStopProfiler,
+            WaitResult outResult, Configuration config, int userId) {
         // Refuse possible leaked file descriptors
         if (intent != null && intent.hasFileDescriptors()) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
-
         boolean componentSpecified = intent.getComponent() != null;
 
         // Don't modify the client's object!
@@ -2835,6 +2882,7 @@
         // Collect information about the target of the Intent.
         ActivityInfo aInfo = resolveActivity(intent, resolvedType, debug,
                 profileFile, profileFd, autoStopProfiler);
+        aInfo = mService.getActivityInfoForUser(aInfo, userId);
 
         synchronized (mService) {
             int callingPid;
@@ -2915,6 +2963,7 @@
                                         PackageManager.MATCH_DEFAULT_ONLY
                                         | ActivityManagerService.STOCK_PM_FLAGS);
                             aInfo = rInfo != null ? rInfo.activityInfo : null;
+                            aInfo = mService.getActivityInfoForUser(aInfo, userId);
                         } catch (RemoteException e) {
                             aInfo = null;
                         }
@@ -2977,7 +3026,8 @@
     }
     
     final int startActivities(IApplicationThread caller, int callingUid,
-            Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+            Intent[] intents,
+            String[] resolvedTypes, IBinder resultTo, int userId) {
         if (intents == null) {
             throw new NullPointerException("intents is null");
         }
@@ -3022,6 +3072,8 @@
                     // Collect information about the target of the Intent.
                     ActivityInfo aInfo = resolveActivity(intent, resolvedTypes[i], false,
                             null, null, false);
+                    // TODO: New, check if this is correct
+                    aInfo = mService.getActivityInfoForUser(aInfo, userId);
 
                     if (mMainStack && aInfo != null && (aInfo.applicationInfo.flags
                             & ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index abd2a1f..3b6a97c 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -24,6 +24,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserId;
 import android.util.Slog;
 
 import java.io.PrintWriter;
@@ -248,7 +249,8 @@
                             owner.broadcastIntentInPackage(key.packageName, uid,
                                     finalIntent, resolvedType,
                                     finishedReceiver, code, null, null,
-                                    requiredPermission, (finishedReceiver != null), false);
+                                requiredPermission, (finishedReceiver != null), false, UserId
+                                        .getUserId(uid));
                             sendFinish = false;
                         } catch (RuntimeException e) {
                             Slog.w(ActivityManagerService.TAG,
diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java
new file mode 100644
index 0000000..44e7ecc
--- /dev/null
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2011 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.am;
+
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Process;
+import android.os.UserId;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Keeps track of content providers by authority (name) and class. It separates the mapping by
+ * user and ones that are not user-specific (system providers).
+ */
+public class ProviderMap {
+
+    private static final String TAG = "ProviderMap";
+
+    private static final boolean DBG = false;
+
+    private final HashMap<String, ContentProviderRecord> mGlobalByName
+            = new HashMap<String, ContentProviderRecord>();
+    private final HashMap<ComponentName, ContentProviderRecord> mGlobalByClass
+            = new HashMap<ComponentName, ContentProviderRecord>();
+
+    private final SparseArray<HashMap<String, ContentProviderRecord>> mProvidersByNamePerUser
+            = new SparseArray<HashMap<String, ContentProviderRecord>>();
+    private final SparseArray<HashMap<ComponentName, ContentProviderRecord>> mProvidersByClassPerUser
+            = new SparseArray<HashMap<ComponentName, ContentProviderRecord>>();
+
+    ContentProviderRecord getProviderByName(String name) {
+        return getProviderByName(name, -1);
+    }
+
+    ContentProviderRecord getProviderByName(String name, int userId) {
+        if (DBG) {
+            Slog.i(TAG, "getProviderByName: " + name + " , callingUid = " + Binder.getCallingUid());
+        }
+        // Try to find it in the global list
+        ContentProviderRecord record = mGlobalByName.get(name);
+        if (record != null) {
+            return record;
+        }
+
+        // Check the current user's list
+        return getProvidersByName(userId).get(name);
+    }
+
+    ContentProviderRecord getProviderByClass(ComponentName name) {
+        return getProviderByClass(name, -1);
+    }
+
+    ContentProviderRecord getProviderByClass(ComponentName name, int userId) {
+        if (DBG) {
+            Slog.i(TAG, "getProviderByClass: " + name + ", callingUid = " + Binder.getCallingUid());
+        }
+        // Try to find it in the global list
+        ContentProviderRecord record = mGlobalByClass.get(name);
+        if (record != null) {
+            return record;
+        }
+
+        // Check the current user's list
+        return getProvidersByClass(userId).get(name);
+    }
+
+    void putProviderByName(String name, ContentProviderRecord record) {
+        if (DBG) {
+            Slog.i(TAG, "putProviderByName: " + name + " , callingUid = " + Binder.getCallingUid()
+                + ", record uid = " + record.appInfo.uid);
+        }
+        if (record.appInfo.uid < Process.FIRST_APPLICATION_UID) {
+            mGlobalByName.put(name, record);
+        } else {
+            final int userId = UserId.getUserId(record.appInfo.uid);
+            getProvidersByName(userId).put(name, record);
+        }
+    }
+
+    void putProviderByClass(ComponentName name, ContentProviderRecord record) {
+        if (DBG) {
+            Slog.i(TAG, "putProviderByClass: " + name + " , callingUid = " + Binder.getCallingUid()
+                + ", record uid = " + record.appInfo.uid);
+        }
+        if (record.appInfo.uid < Process.FIRST_APPLICATION_UID) {
+            mGlobalByClass.put(name, record);
+        } else {
+            final int userId = UserId.getUserId(record.appInfo.uid);
+            getProvidersByClass(userId).put(name, record);
+        }
+    }
+
+    void removeProviderByName(String name, int optionalUserId) {
+        if (mGlobalByName.containsKey(name)) {
+            if (DBG)
+                Slog.i(TAG, "Removing from globalByName name=" + name);
+            mGlobalByName.remove(name);
+        } else {
+            // TODO: Verify this works, i.e., the caller happens to be from the correct user
+            if (DBG)
+                Slog.i(TAG,
+                        "Removing from providersByName name=" + name + " user="
+                        + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
+            getProvidersByName(optionalUserId).remove(name);
+        }
+    }
+
+    void removeProviderByClass(ComponentName name, int optionalUserId) {
+        if (mGlobalByClass.containsKey(name)) {
+            if (DBG)
+                Slog.i(TAG, "Removing from globalByClass name=" + name);
+            mGlobalByClass.remove(name);
+        } else {
+            if (DBG)
+                Slog.i(TAG,
+                        "Removing from providersByClass name=" + name + " user="
+                        + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
+            getProvidersByClass(optionalUserId).remove(name);
+        }
+    }
+
+    private HashMap<String, ContentProviderRecord> getProvidersByName(int optionalUserId) {
+        final int userId = optionalUserId >= 0
+                ? optionalUserId : Binder.getOrigCallingUser();
+        final HashMap<String, ContentProviderRecord> map = mProvidersByNamePerUser.get(userId);
+        if (map == null) {
+            HashMap<String, ContentProviderRecord> newMap = new HashMap<String, ContentProviderRecord>();
+            mProvidersByNamePerUser.put(userId, newMap);
+            return newMap;
+        } else {
+            return map;
+        }
+    }
+
+    private HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int optionalUserId) {
+        final int userId = optionalUserId >= 0
+                ? optionalUserId : Binder.getOrigCallingUser();
+        final HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.get(userId);
+        if (map == null) {
+            HashMap<ComponentName, ContentProviderRecord> newMap = new HashMap<ComponentName, ContentProviderRecord>();
+            mProvidersByClassPerUser.put(userId, newMap);
+            return newMap;
+        } else {
+            return map;
+        }
+    }
+
+    private void dumpProvidersByClassLocked(PrintWriter pw, boolean dumpAll,
+            HashMap<ComponentName, ContentProviderRecord> map) {
+        Iterator<Map.Entry<ComponentName, ContentProviderRecord>> it = map.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry<ComponentName, ContentProviderRecord> e = it.next();
+            ContentProviderRecord r = e.getValue();
+            if (dumpAll) {
+                pw.print("  * ");
+                pw.println(r);
+                r.dump(pw, "    ");
+            } else {
+                pw.print("  * ");
+                pw.print(r.name.toShortString());
+                /*
+                if (r.app != null) {
+                    pw.println(":");
+                    pw.print("      ");
+                    pw.println(r.app);
+                } else {
+                    pw.println();
+                }
+                */
+            }
+        }
+    }
+
+    private void dumpProvidersByNameLocked(PrintWriter pw,
+            HashMap<String, ContentProviderRecord> map) {
+        Iterator<Map.Entry<String, ContentProviderRecord>> it = map.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry<String, ContentProviderRecord> e = it.next();
+            ContentProviderRecord r = e.getValue();
+            pw.print("  ");
+            pw.print(e.getKey());
+            pw.print(": ");
+            pw.println(r);
+        }
+    }
+
+    void dumpProvidersLocked(PrintWriter pw, boolean dumpAll) {
+        boolean needSep = false;
+        if (mGlobalByClass.size() > 0) {
+            if (needSep)
+                pw.println(" ");
+            pw.println("  Published content providers (by class):");
+            dumpProvidersByClassLocked(pw, dumpAll, mGlobalByClass);
+            pw.println(" ");
+        }
+
+        if (mProvidersByClassPerUser.size() > 1) {
+            for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
+                HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i);
+                pw.println("  User " + mProvidersByClassPerUser.keyAt(i) + ":");
+                dumpProvidersByClassLocked(pw, dumpAll, map);
+                pw.println(" ");
+            }
+        } else if (mProvidersByClassPerUser.size() == 1) {
+            HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(0);
+            dumpProvidersByClassLocked(pw, dumpAll, map);
+        }
+        needSep = true;
+
+        if (dumpAll) {
+            pw.println(" ");
+            pw.println("  Authority to provider mappings:");
+            dumpProvidersByNameLocked(pw, mGlobalByName);
+
+            for (int i = 0; i < mProvidersByNamePerUser.size(); i++) {
+                if (i > 0) {
+                    pw.println("  User " + mProvidersByNamePerUser.keyAt(i) + ":");
+                }
+                dumpProvidersByNameLocked(pw, mProvidersByNamePerUser.valueAt(i));
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 257113b..75ba947 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -25,11 +25,13 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserId;
 import android.util.Slog;
 import android.util.TimeUtils;
 
@@ -60,6 +62,7 @@
                             // all information about the service.
     final ApplicationInfo appInfo;
                             // information about service's app.
+    final int userId;       // user that this service is running as
     final String packageName; // the package implementing intent's component
     final String processName; // process where this component wants to run
     final String permission;// permission needed to access service
@@ -289,6 +292,7 @@
         this.restarter = restarter;
         createTime = SystemClock.elapsedRealtime();
         lastActivity = SystemClock.uptimeMillis();
+        userId = UserId.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 de3129b..47ec218 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -19,7 +19,9 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
+import android.os.UserId;
 
 import java.io.PrintWriter;
 
@@ -37,6 +39,7 @@
     boolean askedCompatMode;// Have asked the user about compat mode for this task.
 
     String stringName;      // caching of toString() result.
+    int userId; // user for which this task was created
     
     TaskRecord(int _taskId, ActivityInfo info, Intent _intent) {
         taskId = _taskId;
@@ -84,13 +87,17 @@
                 origActivity = new ComponentName(info.packageName, info.name);
             }
         }
-        
+
         if (intent != null &&
                 (intent.getFlags()&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
             // Once we are set to an Intent with this flag, we count this
             // task as having a true root activity.
             rootWasReset = true;
         }
+
+        if (info.applicationInfo != null) {
+            userId = UserId.getUserId(info.applicationInfo.uid);
+        }
     }
     
     void dump(PrintWriter pw, String prefix) {
@@ -154,6 +161,8 @@
         } else {
             sb.append(" ??");
         }
+        sb.append(" U ");
+        sb.append(userId);
         sb.append('}');
         return stringName = sb.toString();
     }
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index a71ccb5..9772d6a 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -48,6 +48,7 @@
 import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
 import static android.net.NetworkTemplate.MATCH_WIFI;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
+import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
@@ -154,10 +155,7 @@
     private static final int VERSION_ADDED_SNOOZE = 2;
     private static final int VERSION_ADDED_RESTRICT_BACKGROUND = 3;
     private static final int VERSION_ADDED_METERED = 4;
-
-    private static final long KB_IN_BYTES = 1024;
-    private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
-    private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
+    private static final int VERSION_SPLIT_SNOOZE = 5;
 
     // @VisibleForTesting
     public static final int TYPE_WARNING = 0x1;
@@ -176,6 +174,8 @@
     private static final String ATTR_WARNING_BYTES = "warningBytes";
     private static final String ATTR_LIMIT_BYTES = "limitBytes";
     private static final String ATTR_LAST_SNOOZE = "lastSnooze";
+    private static final String ATTR_LAST_WARNING_SNOOZE = "lastWarningSnooze";
+    private static final String ATTR_LAST_LIMIT_SNOOZE = "lastLimitSnooze";
     private static final String ATTR_METERED = "metered";
     private static final String ATTR_UID = "uid";
     private static final String ATTR_POLICY = "policy";
@@ -184,7 +184,9 @@
 
     // @VisibleForTesting
     public static final String ACTION_ALLOW_BACKGROUND =
-            "com.android.server.action.ACTION_ALLOW_BACKGROUND";
+            "com.android.server.net.action.ALLOW_BACKGROUND";
+    public static final String ACTION_SNOOZE_WARNING =
+            "com.android.server.net.action.SNOOZE_WARNING";
 
     private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;
 
@@ -333,6 +335,11 @@
         final IntentFilter allowFilter = new IntentFilter(ACTION_ALLOW_BACKGROUND);
         mContext.registerReceiver(mAllowReceiver, allowFilter, MANAGE_NETWORK_POLICY, mHandler);
 
+        // listen for snooze warning from notifications
+        final IntentFilter snoozeWarningFilter = new IntentFilter(ACTION_SNOOZE_WARNING);
+        mContext.registerReceiver(mSnoozeWarningReceiver, snoozeWarningFilter,
+                MANAGE_NETWORK_POLICY, mHandler);
+
     }
 
     private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
@@ -418,6 +425,21 @@
     };
 
     /**
+     * Receiver that watches for {@link Notification} control of
+     * {@link NetworkPolicy#lastWarningSnooze}.
+     */
+    private BroadcastReceiver mSnoozeWarningReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // on background handler thread, and verified MANAGE_NETWORK_POLICY
+            // permission above.
+
+            final NetworkTemplate template = intent.getParcelableExtra(EXTRA_NETWORK_TEMPLATE);
+            performSnooze(template, TYPE_WARNING);
+        }
+    };
+
+    /**
      * Observer that watches for {@link INetworkManagementService} alerts.
      */
     private INetworkManagementEventObserver mAlertObserver = new NetworkAlertObserver() {
@@ -458,7 +480,7 @@
             final long totalBytes = getTotalBytes(policy.template, start, end);
 
             if (policy.isOverLimit(totalBytes)) {
-                if (policy.lastSnooze >= start) {
+                if (policy.lastLimitSnooze >= start) {
                     enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes);
                 } else {
                     enqueueNotification(policy, TYPE_LIMIT, totalBytes);
@@ -468,7 +490,7 @@
             } else {
                 notifyUnderLimitLocked(policy.template);
 
-                if (policy.warningBytes != WARNING_DISABLED && totalBytes >= policy.warningBytes) {
+                if (policy.isOverWarning(totalBytes) && policy.lastWarningSnooze < start) {
                     enqueueNotification(policy, TYPE_WARNING, totalBytes);
                 }
             }
@@ -534,7 +556,7 @@
         final String tag = buildNotificationTag(policy, type);
         final Notification.Builder builder = new Notification.Builder(mContext);
         builder.setOnlyAlertOnce(true);
-        builder.setOngoing(true);
+        builder.setWhen(0L);
 
         final Resources res = mContext.getResources();
         switch (type) {
@@ -547,9 +569,14 @@
                 builder.setContentTitle(title);
                 builder.setContentText(body);
 
-                final Intent intent = buildViewDataUsageIntent(policy.template);
+                final Intent snoozeIntent = buildSnoozeWarningIntent(policy.template);
+                builder.setDeleteIntent(PendingIntent.getBroadcast(
+                        mContext, 0, snoozeIntent, PendingIntent.FLAG_UPDATE_CURRENT));
+
+                final Intent viewIntent = buildViewDataUsageIntent(policy.template);
                 builder.setContentIntent(PendingIntent.getActivity(
-                        mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
+                        mContext, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT));
+
                 break;
             }
             case TYPE_LIMIT: {
@@ -574,6 +601,7 @@
                         break;
                 }
 
+                builder.setOngoing(true);
                 builder.setSmallIcon(R.drawable.stat_notify_disabled);
                 builder.setTicker(title);
                 builder.setContentTitle(title);
@@ -608,6 +636,7 @@
                         break;
                 }
 
+                builder.setOngoing(true);
                 builder.setSmallIcon(R.drawable.stat_notify_error);
                 builder.setTicker(title);
                 builder.setContentTitle(title);
@@ -720,10 +749,11 @@
             final long totalBytes = getTotalBytes(policy.template, start, end);
 
             // disable data connection when over limit and not snoozed
-            final boolean overLimit = policy.isOverLimit(totalBytes) && policy.lastSnooze < start;
-            final boolean enabled = !overLimit;
+            final boolean overLimitWithoutSnooze = policy.isOverLimit(totalBytes)
+                    && policy.lastLimitSnooze < start;
+            final boolean networkEnabled = !overLimitWithoutSnooze;
 
-            setNetworkTemplateEnabled(policy.template, enabled);
+            setNetworkTemplateEnabled(policy.template, networkEnabled);
         }
     }
 
@@ -827,7 +857,7 @@
                     // metered network, but no policy limit; we still need to
                     // restrict apps, so push really high quota.
                     quotaBytes = Long.MAX_VALUE;
-                } else if (policy.lastSnooze >= start) {
+                } else if (policy.lastLimitSnooze >= start) {
                     // snoozing past quota, but we still need to restrict apps,
                     // so push really high quota.
                     quotaBytes = Long.MAX_VALUE;
@@ -896,8 +926,8 @@
             final int cycleDay = time.monthDay;
 
             final NetworkTemplate template = buildTemplateMobileAll(subscriberId);
-            mNetworkPolicy.put(template, new NetworkPolicy(
-                    template, cycleDay, warningBytes, LIMIT_DISABLED, SNOOZE_NEVER, true));
+            mNetworkPolicy.put(template, new NetworkPolicy(template, cycleDay, warningBytes,
+                    LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER, true));
             writePolicyLocked();
         }
     }
@@ -935,11 +965,13 @@
                         final int cycleDay = readIntAttribute(in, ATTR_CYCLE_DAY);
                         final long warningBytes = readLongAttribute(in, ATTR_WARNING_BYTES);
                         final long limitBytes = readLongAttribute(in, ATTR_LIMIT_BYTES);
-                        final long lastSnooze;
-                        if (version >= VERSION_ADDED_SNOOZE) {
-                            lastSnooze = readLongAttribute(in, ATTR_LAST_SNOOZE);
+                        final long lastLimitSnooze;
+                        if (version >= VERSION_SPLIT_SNOOZE) {
+                            lastLimitSnooze = readLongAttribute(in, ATTR_LAST_LIMIT_SNOOZE);
+                        } else if (version >= VERSION_ADDED_SNOOZE) {
+                            lastLimitSnooze = readLongAttribute(in, ATTR_LAST_SNOOZE);
                         } else {
-                            lastSnooze = SNOOZE_NEVER;
+                            lastLimitSnooze = SNOOZE_NEVER;
                         }
                         final boolean metered;
                         if (version >= VERSION_ADDED_METERED) {
@@ -955,11 +987,18 @@
                                     metered = false;
                             }
                         }
+                        final long lastWarningSnooze;
+                        if (version >= VERSION_SPLIT_SNOOZE) {
+                            lastWarningSnooze = readLongAttribute(in, ATTR_LAST_WARNING_SNOOZE);
+                        } else {
+                            lastWarningSnooze = SNOOZE_NEVER;
+                        }
 
                         final NetworkTemplate template = new NetworkTemplate(
                                 networkTemplate, subscriberId);
-                        mNetworkPolicy.put(template, new NetworkPolicy(
-                                template, cycleDay, warningBytes, limitBytes, lastSnooze, metered));
+                        mNetworkPolicy.put(template, new NetworkPolicy(template, cycleDay,
+                                warningBytes, limitBytes, lastWarningSnooze, lastLimitSnooze,
+                                metered));
 
                     } else if (TAG_UID_POLICY.equals(tag)) {
                         final int uid = readIntAttribute(in, ATTR_UID);
@@ -1014,7 +1053,7 @@
             out.startDocument(null, true);
 
             out.startTag(null, TAG_POLICY_LIST);
-            writeIntAttribute(out, ATTR_VERSION, VERSION_ADDED_METERED);
+            writeIntAttribute(out, ATTR_VERSION, VERSION_SPLIT_SNOOZE);
             writeBooleanAttribute(out, ATTR_RESTRICT_BACKGROUND, mRestrictBackground);
 
             // write all known network policies
@@ -1030,7 +1069,8 @@
                 writeIntAttribute(out, ATTR_CYCLE_DAY, policy.cycleDay);
                 writeLongAttribute(out, ATTR_WARNING_BYTES, policy.warningBytes);
                 writeLongAttribute(out, ATTR_LIMIT_BYTES, policy.limitBytes);
-                writeLongAttribute(out, ATTR_LAST_SNOOZE, policy.lastSnooze);
+                writeLongAttribute(out, ATTR_LAST_WARNING_SNOOZE, policy.lastWarningSnooze);
+                writeLongAttribute(out, ATTR_LAST_LIMIT_SNOOZE, policy.lastLimitSnooze);
                 writeBooleanAttribute(out, ATTR_METERED, policy.metered);
                 out.endTag(null, TAG_NETWORK_POLICY);
             }
@@ -1141,9 +1181,12 @@
     }
 
     @Override
-    public void snoozePolicy(NetworkTemplate template) {
+    public void snoozeLimit(NetworkTemplate template) {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+        performSnooze(template, TYPE_LIMIT);
+    }
 
+    private void performSnooze(NetworkTemplate template, int type) {
         maybeRefreshTrustedTime();
         final long currentTime = currentTimeMillis();
         synchronized (mRulesLock) {
@@ -1153,7 +1196,16 @@
                 throw new IllegalArgumentException("unable to find policy for " + template);
             }
 
-            policy.lastSnooze = currentTime;
+            switch (type) {
+                case TYPE_WARNING:
+                    policy.lastWarningSnooze = currentTime;
+                    break;
+                case TYPE_LIMIT:
+                    policy.lastLimitSnooze = currentTime;
+                    break;
+                default:
+                    throw new IllegalArgumentException("unexpected type");
+            }
 
             updateNetworkEnabledLocked();
             updateNetworkRulesLocked();
@@ -1246,12 +1298,17 @@
         }
 
         synchronized (mRulesLock) {
-            if (argSet.contains("unsnooze")) {
+            if (argSet.contains("--unsnooze")) {
                 for (NetworkPolicy policy : mNetworkPolicy.values()) {
-                    policy.lastSnooze = SNOOZE_NEVER;
+                    policy.clearSnooze();
                 }
+
+                updateNetworkEnabledLocked();
+                updateNetworkRulesLocked();
+                updateNotificationsLocked();
                 writePolicyLocked();
-                fout.println("Wiped snooze timestamps");
+
+                fout.println("Cleared snooze timestamps");
                 return;
             }
 
@@ -1599,6 +1656,12 @@
         return new Intent(ACTION_ALLOW_BACKGROUND);
     }
 
+    private static Intent buildSnoozeWarningIntent(NetworkTemplate template) {
+        final Intent intent = new Intent(ACTION_SNOOZE_WARNING);
+        intent.putExtra(EXTRA_NETWORK_TEMPLATE, template);
+        return intent;
+    }
+
     private static Intent buildNetworkOverLimitIntent(NetworkTemplate template) {
         final Intent intent = new Intent();
         intent.setComponent(new ComponentName(
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 7930caf..c9b67fc 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -34,6 +34,7 @@
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.NetworkTemplate.buildTemplateWifi;
+import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.provider.Settings.Secure.NETSTATS_DEV_BUCKET_DURATION;
 import static android.provider.Settings.Secure.NETSTATS_DEV_DELETE_AGE;
 import static android.provider.Settings.Secure.NETSTATS_DEV_PERSIST_BYTES;
@@ -121,6 +122,7 @@
 
     private static final int MSG_PERFORM_POLL = 1;
     private static final int MSG_UPDATE_IFACES = 2;
+    private static final int MSG_REGISTER_GLOBAL_ALERT = 3;
 
     /** Flags to control detail level of poll event. */
     private static final int FLAG_PERSIST_NETWORK = 0x1;
@@ -152,10 +154,6 @@
 
     private PendingIntent mPollIntent;
 
-    private static final long KB_IN_BYTES = 1024;
-    private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES;
-    private static final long GB_IN_BYTES = 1024 * MB_IN_BYTES;
-
     private static final String PREFIX_DEV = "dev";
     private static final String PREFIX_UID = "uid";
     private static final String PREFIX_UID_TAG = "uid_tag";
@@ -600,7 +598,7 @@
                 mHandler.obtainMessage(MSG_PERFORM_POLL, flags, 0).sendToTarget();
 
                 // re-arm global alert for next update
-                registerGlobalAlert();
+                mHandler.obtainMessage(MSG_REGISTER_GLOBAL_ALERT).sendToTarget();
             }
         }
     };
@@ -951,6 +949,10 @@
                     updateIfaces();
                     return true;
                 }
+                case MSG_REGISTER_GLOBAL_ALERT: {
+                    registerGlobalAlert();
+                    return true;
+                }
                 default: {
                     return false;
                 }
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index 11ccd60..9b1973e 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -277,6 +277,27 @@
         return execute(builder.toString());
     }
 
+    /**
+     * Clone all the package data directories from srcUserId to targetUserId. If copyData is true,
+     * some of the data is also copied, otherwise just empty directories are created with the
+     * correct access rights.
+     * @param srcUserId user to copy the data directories from
+     * @param targetUserId user to copy the data directories to
+     * @param copyData whether the data itself is to be copied. If false, empty directories are
+     * created.
+     * @return success/error code
+     */
+    public int cloneUserData(int srcUserId, int targetUserId, boolean copyData) {
+        StringBuilder builder = new StringBuilder("cloneuserdata");
+        builder.append(' ');
+        builder.append(srcUserId);
+        builder.append(' ');
+        builder.append(targetUserId);
+        builder.append(' ');
+        builder.append(copyData ? '1' : '0');
+        return execute(builder.toString());
+    }
+
     public boolean ping() {
         if (execute("ping") < 0) {
             return false;
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 090ca64..38c128c 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -92,6 +92,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserId;
 import android.security.SystemKeyStore;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
@@ -1743,12 +1744,16 @@
     }
 
     public ActivityInfo getActivityInfo(ComponentName component, int flags) {
+        return getActivityInfo(component, flags, Binder.getOrigCallingUser());
+    }
+
+    ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
         synchronized (mPackages) {
             PackageParser.Activity a = mActivities.mActivities.get(component);
 
             if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
             if (a != null && mSettings.isEnabledLPr(a.info, flags)) {
-                return PackageParser.generateActivityInfo(a, flags);
+                return PackageParser.generateActivityInfo(a, flags, userId);
             }
             if (mResolveComponentName.equals(component)) {
                 return mResolveActivity;
@@ -1758,36 +1763,48 @@
     }
 
     public ActivityInfo getReceiverInfo(ComponentName component, int flags) {
+        return getReceiverInfo(component, flags, Binder.getOrigCallingUser());
+    }
+
+    ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
         synchronized (mPackages) {
             PackageParser.Activity a = mReceivers.mActivities.get(component);
             if (DEBUG_PACKAGE_INFO) Log.v(
                 TAG, "getReceiverInfo " + component + ": " + a);
             if (a != null && mSettings.isEnabledLPr(a.info, flags)) {
-                return PackageParser.generateActivityInfo(a, flags);
+                return PackageParser.generateActivityInfo(a, flags, userId);
             }
         }
         return null;
     }
 
     public ServiceInfo getServiceInfo(ComponentName component, int flags) {
+        return getServiceInfo(component, flags, Binder.getOrigCallingUser());
+    }
+
+    ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
         synchronized (mPackages) {
             PackageParser.Service s = mServices.mServices.get(component);
             if (DEBUG_PACKAGE_INFO) Log.v(
                 TAG, "getServiceInfo " + component + ": " + s);
             if (s != null && mSettings.isEnabledLPr(s.info, flags)) {
-                return PackageParser.generateServiceInfo(s, flags);
+                return PackageParser.generateServiceInfo(s, flags, userId);
             }
         }
         return null;
     }
 
     public ProviderInfo getProviderInfo(ComponentName component, int flags) {
+        return getProviderInfo(component, flags, UserId.getUserId(Binder.getCallingUid()));
+    }
+
+    ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) {
         synchronized (mPackages) {
             PackageParser.Provider p = mProvidersByComponent.get(component);
             if (DEBUG_PACKAGE_INFO) Log.v(
                 TAG, "getProviderInfo " + component + ": " + p);
             if (p != null && mSettings.isEnabledLPr(p.info, flags)) {
-                return PackageParser.generateProviderInfo(p, flags);
+                return PackageParser.generateProviderInfo(p, flags, userId);
             }
         }
         return null;
@@ -1850,7 +1867,7 @@
 
     public int checkUidPermission(String permName, int uid) {
         synchronized (mPackages) {
-            Object obj = mSettings.getUserIdLPr(uid);
+            Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid));
             if (obj != null) {
                 GrantedPermissions gp = (GrantedPermissions)obj;
                 if (gp.grantedPermissions.contains(permName)) {
@@ -1881,7 +1898,7 @@
         if (permName != null) {
             BasePermission bp = findPermissionTreeLP(permName);
             if (bp != null) {
-                if (bp.uid == Binder.getCallingUid()) {
+                if (bp.uid == UserId.getAppId(Binder.getCallingUid())) {
                     return bp;
                 }
                 throw new SecurityException("Calling uid "
@@ -2010,6 +2027,9 @@
     }
 
     public int checkUidSignatures(int uid1, int uid2) {
+        // Map to base uids.
+        uid1 = UserId.getAppId(uid1);
+        uid2 = UserId.getAppId(uid2);
         // reader
         synchronized (mPackages) {
             Signature[] s1;
@@ -2067,6 +2087,7 @@
     }
 
     public String[] getPackagesForUid(int uid) {
+        uid = UserId.getAppId(uid);
         // reader
         synchronized (mPackages) {
             Object obj = mSettings.getUserIdLPr(uid);
@@ -2091,7 +2112,7 @@
     public String getNameForUid(int uid) {
         // reader
         synchronized (mPackages) {
-            Object obj = mSettings.getUserIdLPr(uid);
+            Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid));
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
                 return sus.name + ":" + sus.userId;
@@ -2110,7 +2131,7 @@
         // reader
         synchronized (mPackages) {
             final SharedUserSetting suid = mSettings.getSharedUserLPw(sharedUserName, 0, false);
-            if(suid == null) {
+            if (suid == null) {
                 return -1;
             }
             return suid.userId;
@@ -2252,6 +2273,9 @@
                 comp = intent.getComponent();
             }
         }
+
+        final int userId = UserId.getUserId(Binder.getCallingUid());
+
         if (comp != null) {
             final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
             final ActivityInfo ai = getActivityInfo(comp, flags);
@@ -2603,6 +2627,7 @@
             Arrays.sort(keys);
             int i = getContinuationPoint(keys, lastRead);
             final int N = keys.length;
+            final int userId = UserId.getUserId(Binder.getCallingUid());
 
             while (i < N) {
                 final String packageName = keys[i++];
@@ -2616,7 +2641,7 @@
                 } else {
                     final PackageParser.Package p = mPackages.get(packageName);
                     if (p != null) {
-                        ai = PackageParser.generateApplicationInfo(p, flags);
+                        ai = PackageParser.generateApplicationInfo(p, flags, userId);
                     }
                 }
 
@@ -2639,12 +2664,13 @@
         // reader
         synchronized (mPackages) {
             final Iterator<PackageParser.Package> i = mPackages.values().iterator();
+            final int userId = UserId.getUserId(Binder.getCallingUid());
             while (i.hasNext()) {
                 final PackageParser.Package p = i.next();
                 if (p.applicationInfo != null
                         && (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0
                         && (!mSafeMode || isSystemApp(p))) {
-                    finalList.add(PackageParser.generateApplicationInfo(p, flags));
+                    finalList.add(PackageParser.generateApplicationInfo(p, flags, userId));
                 }
             }
         }
@@ -2660,7 +2686,8 @@
                     && mSettings.isEnabledLPr(provider.info, flags)
                     && (!mSafeMode || (provider.info.applicationInfo.flags
                             &ApplicationInfo.FLAG_SYSTEM) != 0)
-                    ? PackageParser.generateProviderInfo(provider, flags)
+                    ? PackageParser.generateProviderInfo(provider, flags,
+                            UserId.getUserId(Binder.getCallingUid()))
                     : null;
         }
     }
@@ -2674,7 +2701,7 @@
         synchronized (mPackages) {
             final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProviders.entrySet()
                     .iterator();
-
+            final int userId = UserId.getUserId(Binder.getCallingUid());
             while (i.hasNext()) {
                 Map.Entry<String, PackageParser.Provider> entry = i.next();
                 PackageParser.Provider p = entry.getValue();
@@ -2683,7 +2710,7 @@
                         && (!mSafeMode || (p.info.applicationInfo.flags
                                 &ApplicationInfo.FLAG_SYSTEM) != 0)) {
                     outNames.add(entry.getKey());
-                    outInfo.add(PackageParser.generateProviderInfo(p, 0));
+                    outInfo.add(PackageParser.generateProviderInfo(p, 0, userId));
                 }
             }
         }
@@ -2696,19 +2723,21 @@
         // reader
         synchronized (mPackages) {
             final Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator();
+            final int userId = UserId.getUserId(Binder.getCallingUid());
             while (i.hasNext()) {
                 final PackageParser.Provider p = i.next();
                 if (p.info.authority != null
                         && (processName == null
                                 || (p.info.processName.equals(processName)
-                                        && p.info.applicationInfo.uid == uid))
+                                        && UserId.getAppId(p.info.applicationInfo.uid)
+                                            == UserId.getAppId(uid)))
                         && mSettings.isEnabledLPr(p.info, flags)
                         && (!mSafeMode
                                 || (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
                     if (finalList == null) {
                         finalList = new ArrayList<ProviderInfo>(3);
                     }
-                    finalList.add(PackageParser.generateProviderInfo(p, flags));
+                    finalList.add(PackageParser.generateProviderInfo(p, flags, userId));
                 }
             }
         }
@@ -4461,8 +4490,8 @@
                 return null;
             }
             final ResolveInfo res = new ResolveInfo();
-            res.activityInfo = PackageParser.generateActivityInfo(activity,
-                    mFlags);
+            res.activityInfo = PackageParser.generateActivityInfo(activity, mFlags,
+                    Binder.getOrigCallingUser());
             if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = info;
             }
@@ -4637,8 +4666,8 @@
                 return null;
             }
             final ResolveInfo res = new ResolveInfo();
-            res.serviceInfo = PackageParser.generateServiceInfo(service,
-                    mFlags);
+            res.serviceInfo = PackageParser.generateServiceInfo(service, mFlags,
+                    Binder.getOrigCallingUser());
             if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = filter;
             }
@@ -4742,8 +4771,10 @@
                     intent.setPackage(targetPkg);
                 }
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                // TODO: Fix the userId argument
                 am.broadcastIntent(null, intent, null, finishedReceiver,
-                        0, null, null, null, finishedReceiver != null, false);
+                        0, null, null, null, finishedReceiver != null, false,
+                        Binder.getOrigCallingUser());
             } catch (RemoteException ex) {
             }
         }
@@ -7584,7 +7615,7 @@
                         "Unknown component: " + packageName
                         + "/" + className);
             }
-            if (!allowedByPermission && (uid != pkgSetting.userId)) {
+            if (!allowedByPermission && (!UserId.isSameApp(uid, pkgSetting.userId))) {
                 throw new SecurityException(
                         "Permission Denial: attempt to change component state from pid="
                         + Binder.getCallingPid()
@@ -8673,4 +8704,8 @@
             return mSettings.getVerifierDeviceIdentityLPw();
         }
     }
+
+    public List<UserInfo> getUsers() {
+        return mUserManager.getUsers();
+    }
 }
diff --git a/services/java/com/android/server/pm/UserManager.java b/services/java/com/android/server/pm/UserManager.java
index 2687728..5eacf4a 100644
--- a/services/java/com/android/server/pm/UserManager.java
+++ b/services/java/com/android/server/pm/UserManager.java
@@ -24,6 +24,7 @@
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.SystemClock;
+import android.os.UserId;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -169,9 +170,10 @@
      * </user>
      */
     private void writeUser(UserInfo userInfo) {
+        FileOutputStream fos = null;
         try {
             final File mUserFile = new File(mUsersDir, userInfo.id + ".xml");
-            final FileOutputStream fos = new FileOutputStream(mUserFile);
+            fos = new FileOutputStream(mUserFile);
             final BufferedOutputStream bos = new BufferedOutputStream(fos);
 
             // XmlSerializer serializer = XmlUtils.serializerInstance();
@@ -193,6 +195,13 @@
             serializer.endDocument();
         } catch (IOException ioe) {
             Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
+        } finally {
+            if (fos != null) {
+                try {
+                    fos.close();
+                } catch (IOException ioe) {
+                }
+            }
         }
     }
 
@@ -205,8 +214,9 @@
      * </users>
      */
     private void writeUserList() {
+        FileOutputStream fos = null;
         try {
-            final FileOutputStream fos = new FileOutputStream(mUserListFile);
+            fos = new FileOutputStream(mUserListFile);
             final BufferedOutputStream bos = new BufferedOutputStream(fos);
 
             // XmlSerializer serializer = XmlUtils.serializerInstance();
@@ -229,6 +239,13 @@
             serializer.endDocument();
         } catch (IOException ioe) {
             Slog.e(LOG_TAG, "Error writing user list");
+        } finally {
+            if (fos != null) {
+                try {
+                    fos.close();
+                } catch (IOException ioe) {
+                }
+            }
         }
     }
 
@@ -330,7 +347,7 @@
             // Don't do it for the primary user, it will become recursive.
             if (userId == 0)
                 continue;
-            mInstaller.createUserData(packageName, PackageManager.getUid(userId, uid),
+            mInstaller.createUserData(packageName, UserId.getUid(userId, uid),
                     userId);
         }
     }
@@ -367,6 +384,8 @@
 
     /**
      * Returns the next available user id, filling in any holes in the ids.
+     * TODO: May not be a good idea to recycle ids, in case it results in confusion
+     * for data and battery stats collection, or unexpected cross-talk.
      * @return
      */
     private int getNextAvailableId() {
@@ -390,14 +409,8 @@
         FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
                 | FileUtils.S_IXOTH, -1, -1);
 
-        // Create the individual data directories
-        for (ApplicationInfo app : apps) {
-            if (app.uid > android.os.Process.FIRST_APPLICATION_UID
-                    && app.uid < PackageManager.PER_USER_RANGE) {
-                mInstaller.createUserData(app.packageName,
-                        PackageManager.getUid(id, app.uid), id);
-            }
-        }
+        mInstaller.cloneUserData(0, id, false);
+
         final long stopTime = SystemClock.elapsedRealtime();
         Log.i(LOG_TAG,
                 "Time to create " + apps.size() + " packages = " + (stopTime - startTime) + "ms");
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 620d74c..04c1c98 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -512,9 +512,11 @@
     WindowState mCurrentFocus = null;
     WindowState mLastFocus = null;
 
-    // This just indicates the window the input method is on top of, not
-    // necessarily the window its input is going to.
+    /** This just indicates the window the input method is on top of, not
+     * necessarily the window its input is going to. */
     WindowState mInputMethodTarget = null;
+
+    /** If true hold off on modifying the animation layer of mInputMethodTarget */
     boolean mInputMethodTargetWaitingAnim;
     int mInputMethodAnimLayerAdjustment;
 
@@ -575,6 +577,29 @@
 
     DragState mDragState = null;
 
+    /** Pulled out of performLayoutAndPlaceSurfacesLockedInner in order to refactor into multiple
+     * methods. */
+    private class LayoutAndSurfaceFields {
+        private boolean mAnimating = false;
+        private boolean mWallpaperForceHidingChanged = false;
+        private boolean mTokenMayBeDrawn = false;
+        private boolean mWallpaperMayChange = false;
+        private boolean mForceHiding = false;
+        private WindowState mDetachedWallpaper = null;
+        private WindowState mWindowAnimationBackground = null;
+        private int mWindowAnimationBackgroundColor = 0;
+        private boolean mOrientationChangeComplete = true;
+        private int mAdjResult = 0;
+        private Session mHoldScreen = null;
+        private boolean mObscured = false;
+        private boolean mBlurring = false;
+        private boolean mDimming = false;
+        private boolean mSyswin = false;
+        private float mScreenBrightness = -1;
+        private float mButtonBrightness = -1;
+    }
+    private LayoutAndSurfaceFields mInnerFields = new LayoutAndSurfaceFields();
+
     final class DragInputEventReceiver extends InputEventReceiver {
         public DragInputEventReceiver(InputChannel inputChannel, Looper looper) {
             super(inputChannel, looper);
@@ -1090,6 +1115,11 @@
         return false;
     }
 
+    /**
+     * Dig through the WindowStates and find the one that the Input Method will target.
+     * @param willMove
+     * @return The index+1 in mWindows of the discovered target.
+     */
     int findDesiredInputMethodWindowIndexLocked(boolean willMove) {
         final ArrayList<WindowState> localmWindows = mWindows;
         final int N = localmWindows.size();
@@ -1122,8 +1152,10 @@
             }
         }
 
+        // Now w is either mWindows[0] or an IME (or null if mWindows is empty).
+
         if (DEBUG_INPUT_METHOD && willMove) Slog.v(TAG, "Proposed new IME target: " + w);
-        
+
         // Now, a special case -- if the last target's window is in the
         // process of exiting, and is above the new target, keep on the
         // last target to avoid flicker.  Consider for example a Dialog with
@@ -1155,8 +1187,7 @@
                 WindowState highestTarget = null;
                 int highestPos = 0;
                 if (token.animating || token.animation != null) {
-                    int pos = 0;
-                    pos = localmWindows.indexOf(curTarget);
+                    int pos = localmWindows.indexOf(curTarget);
                     while (pos >= 0) {
                         WindowState win = localmWindows.get(pos);
                         if (win.mAppToken != token) {
@@ -6218,7 +6249,6 @@
                         final IBinder winBinder = window.asBinder();
                         token = new Binder();
                         mDragState = new DragState(this, token, surface, /*flags*/ 0, winBinder);
-                        mDragState.mSurface = surface;
                         token = mDragState.mToken = new Binder();
 
                         // 5 second timeout for this window to actually begin the drag
@@ -7385,7 +7415,7 @@
         try {
             performLayoutAndPlaceSurfacesLockedInner(recoveringMemory);
 
-            int N = mPendingRemove.size();
+            final int N = mPendingRemove.size();
             if (N > 0) {
                 if (mPendingRemoveTmp.length < N) {
                     mPendingRemoveTmp = new WindowState[N+10];
@@ -7418,9 +7448,9 @@
         }
     }
 
-    private final int performLayoutLockedInner(boolean initial, boolean updateInputWindows) {
+    private final void performLayoutLockedInner(boolean initial, boolean updateInputWindows) {
         if (!mLayoutNeeded) {
-            return 0;
+            return;
         }
         
         mLayoutNeeded = false;
@@ -7452,7 +7482,7 @@
         // to another window).
         int topAttached = -1;
         for (i = N-1; i >= 0; i--) {
-            WindowState win = mWindows.get(i);
+            final WindowState win = mWindows.get(i);
 
             // Don't do layout of a window if it is not visible, or
             // soon won't be visible, to avoid wasting time and funky
@@ -7508,7 +7538,7 @@
         // XXX does not deal with windows that are attached to windows
         // that are themselves attached.
         for (i = topAttached; i >= 0; i--) {
-            WindowState win = mWindows.get(i);
+            final WindowState win = mWindows.get(i);
 
             if (win.mLayoutAttached) {
                 if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + win
@@ -7544,7 +7574,7 @@
             mInputMonitor.updateInputWindowsLw(false /*force*/);
         }
 
-        return mPolicy.finishLayoutLw();
+        mPolicy.finishLayoutLw();
     }
 
     void makeWindowFreezingScreenIfNeededLocked(WindowState w) {
@@ -7566,6 +7596,1006 @@
         }
     }
 
+    /**
+     * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
+     * Update animations of all applications, including those associated with exiting/removed apps.
+     *
+     * @param currentTime The time which animations use for calculating transitions.
+     * @param innerDw Width of app window.
+     * @param innerDh Height of app window.
+     * @return true if rotation has stopped, false otherwise
+     */
+    private boolean updateAppsAndRotationAnimationsLocked(long currentTime,
+                                                          int innerDw, int innerDh) {
+        int i;
+        final int NAT = mAppTokens.size();
+        for (i=0; i<NAT; i++) {
+            if (mAppTokens.get(i).stepAnimationLocked(currentTime,
+                    innerDw, innerDh)) {
+                mInnerFields.mAnimating = true;
+            }
+        }
+        final int NEAT = mExitingAppTokens.size();
+        for (i=0; i<NEAT; i++) {
+            if (mExitingAppTokens.get(i).stepAnimationLocked(currentTime,
+                    innerDw, innerDh)) {
+                mInnerFields.mAnimating = true;
+            }
+        }
+
+        boolean updateRotation = false;
+        if (mScreenRotationAnimation != null) {
+            if (mScreenRotationAnimation.isAnimating()) {
+                if (mScreenRotationAnimation.stepAnimation(currentTime)) {
+                    mInnerFields.mAnimating = true;
+                } else {
+                    updateRotation = true;
+                }
+            }
+        }
+
+        return updateRotation;
+    }
+
+    /**
+     * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
+     *
+     * @param currentTime The time which animations use for calculating transitions.
+     * @param innerDw Width of app window.
+     * @param innerDh Height of app window.
+     */
+    private void updateWindowsAndWallpaperLocked(final long currentTime,
+                                                 final int innerDw, final int innerDh) {
+        int i;
+        final int N = mWindows.size();
+
+        for (i=N-1; i>=0; i--) {
+            WindowState w = mWindows.get(i);
+
+            final WindowManager.LayoutParams attrs = w.mAttrs;
+
+            if (w.mSurface != null) {
+                // Take care of the window being ready to display.
+                if (w.commitFinishDrawingLocked(currentTime)) {
+                    if ((w.mAttrs.flags
+                            & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
+                        if (DEBUG_WALLPAPER) Slog.v(TAG,
+                                "First draw done in potential wallpaper target " + w);
+                        mInnerFields.mWallpaperMayChange = true;
+                    }
+                }
+
+                final boolean wasAnimating = w.mAnimating;
+
+                int animDw = innerDw;
+                int animDh = innerDh;
+
+                // If the window has moved due to its containing
+                // content frame changing, then we'd like to animate
+                // it.  The checks here are ordered by what is least
+                // likely to be true first.
+                if (w.shouldAnimateMove()) {
+                    // Frame has moved, containing content frame
+                    // has also moved, and we're not currently animating...
+                    // let's do something.
+                    Animation a = AnimationUtils.loadAnimation(mContext,
+                            com.android.internal.R.anim.window_move_from_decor);
+                    w.setAnimation(a);
+                    animDw = w.mLastFrame.left - w.mFrame.left;
+                    animDh = w.mLastFrame.top - w.mFrame.top;
+                }
+
+                // Execute animation.
+                final boolean nowAnimating = w.stepAnimationLocked(currentTime,
+                        animDw, animDh);
+
+                // If this window is animating, make a note that we have
+                // an animating window and take care of a request to run
+                // a detached wallpaper animation.
+                if (nowAnimating) {
+                    if (w.mAnimation != null) {
+                        if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0
+                                && w.mAnimation.getDetachWallpaper()) {
+                            mInnerFields.mDetachedWallpaper = w;
+                        }
+                        if (w.mAnimation.getBackgroundColor() != 0) {
+                            if (mInnerFields.mWindowAnimationBackground == null
+                                    || (w.mAnimLayer <
+                                            mInnerFields.mWindowAnimationBackground.mAnimLayer)) {
+                                mInnerFields.mWindowAnimationBackground = w;
+                                mInnerFields.mWindowAnimationBackgroundColor =
+                                        w.mAnimation.getBackgroundColor();
+                            }
+                        }
+                    }
+                    mInnerFields.mAnimating = true;
+                }
+
+                // If this window's app token is running a detached wallpaper
+                // animation, make a note so we can ensure the wallpaper is
+                // displayed behind it.
+                if (w.mAppToken != null && w.mAppToken.animation != null
+                        && w.mAppToken.animating) {
+                    if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0
+                            && w.mAppToken.animation.getDetachWallpaper()) {
+                        mInnerFields.mDetachedWallpaper = w;
+                    }
+                    if (w.mAppToken.animation.getBackgroundColor() != 0) {
+                        if (mInnerFields.mWindowAnimationBackground == null
+                                || (w.mAnimLayer <
+                                        mInnerFields.mWindowAnimationBackground.mAnimLayer)) {
+                            mInnerFields.mWindowAnimationBackground = w;
+                            mInnerFields.mWindowAnimationBackgroundColor =
+                                    w.mAppToken.animation.getBackgroundColor();
+                        }
+                    }
+                }
+
+                if (wasAnimating && !w.mAnimating && mWallpaperTarget == w) {
+                    mInnerFields.mWallpaperMayChange = true;
+                }
+
+                if (mPolicy.doesForceHide(w, attrs)) {
+                    if (!wasAnimating && nowAnimating) {
+                        if (DEBUG_VISIBILITY) Slog.v(TAG,
+                                "Animation started that could impact force hide: "
+                                + w);
+                        mInnerFields.mWallpaperForceHidingChanged = true;
+                        mFocusMayChange = true;
+                    } else if (w.isReadyForDisplay() && w.mAnimation == null) {
+                        mInnerFields.mForceHiding = true;
+                    }
+                } else if (mPolicy.canBeForceHidden(w, attrs)) {
+                    boolean changed;
+                    if (mInnerFields.mForceHiding) {
+                        changed = w.hideLw(false, false);
+                        if (DEBUG_VISIBILITY && changed) Slog.v(TAG,
+                                "Now policy hidden: " + w);
+                    } else {
+                        changed = w.showLw(false, false);
+                        if (DEBUG_VISIBILITY && changed) Slog.v(TAG,
+                                "Now policy shown: " + w);
+                        if (changed) {
+                            if (mInnerFields.mWallpaperForceHidingChanged
+                                    && w.isVisibleNow() /*w.isReadyForDisplay()*/) {
+                                // Assume we will need to animate.  If
+                                // we don't (because the wallpaper will
+                                // stay with the lock screen), then we will
+                                // clean up later.
+                                Animation a = mPolicy.createForceHideEnterAnimation();
+                                if (a != null) {
+                                    w.setAnimation(a);
+                                }
+                            }
+                            if (mCurrentFocus == null ||
+                                    mCurrentFocus.mLayer < w.mLayer) {
+                                // We are showing on to of the current
+                                // focus, so re-evaluate focus to make
+                                // sure it is correct.
+                                mFocusMayChange = true;
+                            }
+                        }
+                    }
+                    if (changed && (attrs.flags
+                            & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
+                        mInnerFields.mWallpaperMayChange = true;
+                    }
+                }
+
+                mPolicy.animatingWindowLw(w, attrs);
+            }
+
+            final AppWindowToken atoken = w.mAppToken;
+            if (atoken != null && (!atoken.allDrawn || atoken.freezingScreen)) {
+                if (atoken.lastTransactionSequence != mTransactionSequence) {
+                    atoken.lastTransactionSequence = mTransactionSequence;
+                    atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
+                    atoken.startingDisplayed = false;
+                }
+                if ((w.isOnScreen() || w.mAttrs.type
+                        == WindowManager.LayoutParams.TYPE_BASE_APPLICATION)
+                        && !w.mExiting && !w.mDestroying) {
+                    if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) {
+                        Slog.v(TAG, "Eval win " + w + ": isDrawn="
+                                + w.isDrawnLw()
+                                + ", isAnimating=" + w.isAnimating());
+                        if (!w.isDrawnLw()) {
+                            Slog.v(TAG, "Not displayed: s=" + w.mSurface
+                                    + " pv=" + w.mPolicyVisibility
+                                    + " dp=" + w.mDrawPending
+                                    + " cdp=" + w.mCommitDrawPending
+                                    + " ah=" + w.mAttachedHidden
+                                    + " th=" + atoken.hiddenRequested
+                                    + " a=" + w.mAnimating);
+                        }
+                    }
+                    if (w != atoken.startingWindow) {
+                        if (!atoken.freezingScreen || !w.mAppFreezing) {
+                            atoken.numInterestingWindows++;
+                            if (w.isDrawnLw()) {
+                                atoken.numDrawnWindows++;
+                                if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) Slog.v(TAG,
+                                        "tokenMayBeDrawn: " + atoken
+                                        + " freezingScreen=" + atoken.freezingScreen
+                                        + " mAppFreezing=" + w.mAppFreezing);
+                                mInnerFields.mTokenMayBeDrawn = true;
+                            }
+                        }
+                    } else if (w.isDrawnLw()) {
+                        atoken.startingDisplayed = true;
+                    }
+                }
+            } else if (w.mReadyToShow) {
+                w.performShowLocked();
+            }
+        } // end forall windows
+    }
+
+    /**
+     * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
+     *
+     * @return bitmap indicating if another pass through layout must be made.
+     */
+    private int testTokenMayBeDrawnLocked() {
+        int changes = 0;
+        // See if any windows have been drawn, so they (and others
+        // associated with them) can now be shown.
+        final int NT = mAppTokens.size();
+        for (int i=0; i<NT; i++) {
+            AppWindowToken wtoken = mAppTokens.get(i);
+            if (wtoken.freezingScreen) {
+                int numInteresting = wtoken.numInterestingWindows;
+                if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
+                    if (DEBUG_VISIBILITY) Slog.v(TAG,
+                            "allDrawn: " + wtoken
+                            + " interesting=" + numInteresting
+                            + " drawn=" + wtoken.numDrawnWindows);
+                    wtoken.showAllWindowsLocked();
+                    unsetAppFreezingScreenLocked(wtoken, false, true);
+                    if (DEBUG_ORIENTATION) Slog.i(TAG,
+                            "Setting mOrientationChangeComplete=true because wtoken "
+                            + wtoken + " numInteresting=" + numInteresting
+                            + " numDrawn=" + wtoken.numDrawnWindows);
+                    mInnerFields.mOrientationChangeComplete = true;
+                }
+            } else if (!wtoken.allDrawn) {
+                int numInteresting = wtoken.numInterestingWindows;
+                if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
+                    if (DEBUG_VISIBILITY) Slog.v(TAG,
+                            "allDrawn: " + wtoken
+                            + " interesting=" + numInteresting
+                            + " drawn=" + wtoken.numDrawnWindows);
+                    wtoken.allDrawn = true;
+                    changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
+
+                    // We can now show all of the drawn windows!
+                    if (!mOpeningApps.contains(wtoken)) {
+                        wtoken.showAllWindowsLocked();
+                    }
+                }
+            }
+        }
+
+        return changes;
+    }
+
+    /**
+     * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
+     *
+     * @return bitmap indicating if another pass through layout must be made.
+     */
+    public int handleAppTransitionReadyLocked() {
+        int changes = 0;
+        int i;
+        int NN = mOpeningApps.size();
+        boolean goodToGo = true;
+        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+                "Checking " + NN + " opening apps (frozen="
+                + mDisplayFrozen + " timeout="
+                + mAppTransitionTimeout + ")...");
+        if (!mDisplayFrozen && !mAppTransitionTimeout) {
+            // If the display isn't frozen, wait to do anything until
+            // all of the apps are ready.  Otherwise just go because
+            // we'll unfreeze the display when everyone is ready.
+            for (i=0; i<NN && goodToGo; i++) {
+                AppWindowToken wtoken = mOpeningApps.get(i);
+                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+                        "Check opening app" + wtoken + ": allDrawn="
+                        + wtoken.allDrawn + " startingDisplayed="
+                        + wtoken.startingDisplayed);
+                if (!wtoken.allDrawn && !wtoken.startingDisplayed
+                        && !wtoken.startingMoved) {
+                    goodToGo = false;
+                }
+            }
+        }
+        if (goodToGo) {
+            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
+            int transit = mNextAppTransition;
+            if (mSkipAppTransitionAnimation) {
+                transit = WindowManagerPolicy.TRANSIT_UNSET;
+            }
+            mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
+            mAppTransitionReady = false;
+            mAppTransitionRunning = true;
+            mAppTransitionTimeout = false;
+            mStartingIconInTransition = false;
+            mSkipAppTransitionAnimation = false;
+
+            mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
+
+            // If there are applications waiting to come to the
+            // top of the stack, now is the time to move their windows.
+            // (Note that we don't do apps going to the bottom
+            // here -- we want to keep their windows in the old
+            // Z-order until the animation completes.)
+            if (mToTopApps.size() > 0) {
+                NN = mAppTokens.size();
+                for (i=0; i<NN; i++) {
+                    AppWindowToken wtoken = mAppTokens.get(i);
+                    if (wtoken.sendingToTop) {
+                        wtoken.sendingToTop = false;
+                        moveAppWindowsLocked(wtoken, NN, false);
+                    }
+                }
+                mToTopApps.clear();
+            }
+
+            WindowState oldWallpaper = mWallpaperTarget;
+
+            adjustWallpaperWindowsLocked();
+            mInnerFields.mWallpaperMayChange = false;
+
+            // The top-most window will supply the layout params,
+            // and we will determine it below.
+            LayoutParams animLp = null;
+            int bestAnimLayer = -1;
+            boolean fullscreenAnim = false;
+
+            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+                    "New wallpaper target=" + mWallpaperTarget
+                    + ", lower target=" + mLowerWallpaperTarget
+                    + ", upper target=" + mUpperWallpaperTarget);
+            int foundWallpapers = 0;
+            // Do a first pass through the tokens for two
+            // things:
+            // (1) Determine if both the closing and opening
+            // app token sets are wallpaper targets, in which
+            // case special animations are needed
+            // (since the wallpaper needs to stay static
+            // behind them).
+            // (2) Find the layout params of the top-most
+            // application window in the tokens, which is
+            // what will control the animation theme.
+            final int NC = mClosingApps.size();
+            NN = NC + mOpeningApps.size();
+            for (i=0; i<NN; i++) {
+                AppWindowToken wtoken;
+                int mode;
+                if (i < NC) {
+                    wtoken = mClosingApps.get(i);
+                    mode = 1;
+                } else {
+                    wtoken = mOpeningApps.get(i-NC);
+                    mode = 2;
+                }
+                if (mLowerWallpaperTarget != null) {
+                    if (mLowerWallpaperTarget.mAppToken == wtoken
+                            || mUpperWallpaperTarget.mAppToken == wtoken) {
+                        foundWallpapers |= mode;
+                    }
+                }
+                if (wtoken.appFullscreen) {
+                    WindowState ws = wtoken.findMainWindow();
+                    if (ws != null) {
+                        animLp = ws.mAttrs;
+                        bestAnimLayer = ws.mLayer;
+                        fullscreenAnim = true;
+                    }
+                } else if (!fullscreenAnim) {
+                    WindowState ws = wtoken.findMainWindow();
+                    if (ws != null) {
+                        if (ws.mLayer > bestAnimLayer) {
+                            animLp = ws.mAttrs;
+                            bestAnimLayer = ws.mLayer;
+                        }
+                    }
+                }
+            }
+
+            if (foundWallpapers == 3) {
+                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+                        "Wallpaper animation!");
+                switch (transit) {
+                    case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
+                    case WindowManagerPolicy.TRANSIT_TASK_OPEN:
+                    case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT:
+                        transit = WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN;
+                        break;
+                    case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE:
+                    case WindowManagerPolicy.TRANSIT_TASK_CLOSE:
+                    case WindowManagerPolicy.TRANSIT_TASK_TO_BACK:
+                        transit = WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_CLOSE;
+                        break;
+                }
+                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+                        "New transit: " + transit);
+            } else if (oldWallpaper != null) {
+                // We are transitioning from an activity with
+                // a wallpaper to one without.
+                transit = WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE;
+                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+                        "New transit away from wallpaper: " + transit);
+            } else if (mWallpaperTarget != null) {
+                // We are transitioning from an activity without
+                // a wallpaper to now showing the wallpaper
+                transit = WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN;
+                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+                        "New transit into wallpaper: " + transit);
+            }
+
+            // If all closing windows are obscured, then there is
+            // no need to do an animation.  This is the case, for
+            // example, when this transition is being done behind
+            // the lock screen.
+            if (!mPolicy.allowAppAnimationsLw()) {
+                animLp = null;
+            }
+
+            NN = mOpeningApps.size();
+            for (i=0; i<NN; i++) {
+                AppWindowToken wtoken = mOpeningApps.get(i);
+                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+                        "Now opening app" + wtoken);
+                wtoken.reportedVisible = false;
+                wtoken.inPendingTransaction = false;
+                wtoken.animation = null;
+                setTokenVisibilityLocked(wtoken, animLp, true,
+                        transit, false);
+                wtoken.updateReportedVisibilityLocked();
+                wtoken.waitingToShow = false;
+                wtoken.showAllWindowsLocked();
+            }
+            NN = mClosingApps.size();
+            for (i=0; i<NN; i++) {
+                AppWindowToken wtoken = mClosingApps.get(i);
+                if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+                        "Now closing app" + wtoken);
+                wtoken.inPendingTransaction = false;
+                wtoken.animation = null;
+                setTokenVisibilityLocked(wtoken, animLp, false,
+                        transit, false);
+                wtoken.updateReportedVisibilityLocked();
+                wtoken.waitingToHide = false;
+                // Force the allDrawn flag, because we want to start
+                // this guy's animations regardless of whether it's
+                // gotten drawn.
+                wtoken.allDrawn = true;
+            }
+
+            mNextAppTransitionPackage = null;
+
+            mOpeningApps.clear();
+            mClosingApps.clear();
+
+            // This has changed the visibility of windows, so perform
+            // a new layout to get them all up-to-date.
+            changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT
+                    | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
+            mLayoutNeeded = true;
+            if (!moveInputMethodWindowsIfNeededLocked(true)) {
+                assignLayersLocked();
+            }
+            updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
+                    false /*updateInputWindows*/);
+            mFocusMayChange = false;
+        }
+
+        return changes;
+    }
+
+    /**
+     * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
+     *
+     * @return bitmap indicating if another pass through layout must be made.
+     */
+    private int handleAnimatingAndTransitionLocked() {
+        int changes = 0;
+
+        mAppTransitionRunning = false;
+        // Clear information about apps that were moving.
+        mToBottomApps.clear();
+
+        rebuildAppWindowListLocked();
+        changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
+        mInnerFields.mAdjResult |= ADJUST_WALLPAPER_LAYERS_CHANGED;
+        moveInputMethodWindowsIfNeededLocked(false);
+        mInnerFields.mWallpaperMayChange = true;
+        // Since the window list has been rebuilt, focus might
+        // have to be recomputed since the actual order of windows
+        // might have changed again.
+        mFocusMayChange = true;
+
+        return changes;
+    }
+
+    /**
+     * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
+     *
+     * @return bitmap indicating if another pass through layout must be made.
+     */
+    private int animateAwayWallpaperLocked() {
+        int changes = 0;
+        WindowState oldWallpaper = mWallpaperTarget;
+        if (mLowerWallpaperTarget != null
+                && mLowerWallpaperTarget.mAppToken != null) {
+            if (DEBUG_WALLPAPER) Slog.v(TAG,
+                    "wallpaperForceHiding changed with lower="
+                    + mLowerWallpaperTarget);
+            if (DEBUG_WALLPAPER) Slog.v(TAG,
+                    "hidden=" + mLowerWallpaperTarget.mAppToken.hidden +
+                    " hiddenRequested=" + mLowerWallpaperTarget.mAppToken.hiddenRequested);
+            if (mLowerWallpaperTarget.mAppToken.hidden) {
+                // The lower target has become hidden before we
+                // actually started the animation...  let's completely
+                // re-evaluate everything.
+                mLowerWallpaperTarget = mUpperWallpaperTarget = null;
+                changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
+            }
+        }
+        mInnerFields.mAdjResult |= adjustWallpaperWindowsLocked();
+        mInnerFields.mWallpaperMayChange = false;
+        mInnerFields.mWallpaperForceHidingChanged = false;
+        if (DEBUG_WALLPAPER) Slog.v(TAG, "****** OLD: " + oldWallpaper
+                + " NEW: " + mWallpaperTarget
+                + " LOWER: " + mLowerWallpaperTarget);
+        if (mLowerWallpaperTarget == null) {
+            // Whoops, we don't need a special wallpaper animation.
+            // Clear them out.
+            mInnerFields.mForceHiding = false;
+            for (int i=mWindows.size()-1; i>=0; i--) {
+                WindowState w = mWindows.get(i);
+                if (w.mSurface != null) {
+                    final WindowManager.LayoutParams attrs = w.mAttrs;
+                    if (mPolicy.doesForceHide(w, attrs) && w.isVisibleLw()) {
+                        if (DEBUG_FOCUS) Slog.i(TAG, "win=" + w + " force hides other windows");
+                        mInnerFields.mForceHiding = true;
+                    } else if (mPolicy.canBeForceHidden(w, attrs)) {
+                        if (!w.mAnimating) {
+                            // We set the animation above so it
+                            // is not yet running.
+                            w.clearAnimation();
+                        }
+                    }
+                }
+            }
+        }
+        return changes;
+    }
+
+    /**
+     * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
+     *
+     * @return bitmap indicating if another pass through layout must be made.
+     */
+    private int testWallpaperAndBackgroundLocked() {
+        int changes = 0;
+
+        if (mWindowDetachedWallpaper != mInnerFields.mDetachedWallpaper) {
+            if (DEBUG_WALLPAPER) Slog.v(TAG,
+                    "Detached wallpaper changed from " + mWindowDetachedWallpaper
+                    + " to " + mInnerFields.mDetachedWallpaper);
+            mWindowDetachedWallpaper = mInnerFields.mDetachedWallpaper;
+            mInnerFields.mWallpaperMayChange = true;
+        }
+
+        if (mInnerFields.mWindowAnimationBackgroundColor != 0) {
+            // If the window that wants black is the current wallpaper
+            // target, then the black goes *below* the wallpaper so we
+            // don't cause the wallpaper to suddenly disappear.
+            WindowState target = mInnerFields.mWindowAnimationBackground;
+            if (mWallpaperTarget == mInnerFields.mWindowAnimationBackground
+                    || mLowerWallpaperTarget == mInnerFields.mWindowAnimationBackground
+                    || mUpperWallpaperTarget == mInnerFields.mWindowAnimationBackground) {
+                for (int i=0; i<mWindows.size(); i++) {
+                    WindowState w = mWindows.get(i);
+                    if (w.mIsWallpaper) {
+                        target = w;
+                        break;
+                    }
+                }
+            }
+            if (mWindowAnimationBackgroundSurface == null) {
+                mWindowAnimationBackgroundSurface = new DimSurface(mFxSession);
+            }
+            final int dw = mCurDisplayWidth;
+            final int dh = mCurDisplayHeight;
+            mWindowAnimationBackgroundSurface.show(dw, dh,
+                    target.mAnimLayer - LAYER_OFFSET_DIM,
+                    mInnerFields.mWindowAnimationBackgroundColor);
+        } else if (mWindowAnimationBackgroundSurface != null) {
+            mWindowAnimationBackgroundSurface.hide();
+        }
+
+        if (mInnerFields.mWallpaperMayChange) {
+            if (DEBUG_WALLPAPER) Slog.v(TAG,
+                    "Wallpaper may change!  Adjusting");
+            mInnerFields.mAdjResult |= adjustWallpaperWindowsLocked();
+        }
+
+        if ((mInnerFields.mAdjResult&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
+            if (DEBUG_WALLPAPER) Slog.v(TAG,
+                    "Wallpaper layer changed: assigning layers + relayout");
+            changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
+            assignLayersLocked();
+        } else if ((mInnerFields.mAdjResult&ADJUST_WALLPAPER_VISIBILITY_CHANGED) != 0) {
+            if (DEBUG_WALLPAPER) Slog.v(TAG,
+                    "Wallpaper visibility changed: relayout");
+            changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
+        }
+
+        if (mFocusMayChange) {
+            mFocusMayChange = false;
+            if (updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
+                    false /*updateInputWindows*/)) {
+                changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
+                mInnerFields.mAdjResult = 0;
+            }
+        }
+
+        return changes;
+    }
+
+    /**
+     * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
+     *
+     * @param w WindowState whos Surface is being prepared.
+     * @param recoveringMemory true if the caller will reclaim surface memory on error.
+     */
+    public void prepareSurfaceLocked(final WindowState w, final boolean recoveringMemory) {
+        // XXX NOTE: The logic here could be improved.  We have
+        // the decision about whether to resize a window separated
+        // from whether to hide the surface.  This can cause us to
+        // resize a surface even if we are going to hide it.  You
+        // can see this by (1) holding device in landscape mode on
+        // home screen; (2) tapping browser icon (device will rotate
+        // to landscape; (3) tap home.  The wallpaper will be resized
+        // in step 2 but then immediately hidden, causing us to
+        // have to resize and then redraw it again in step 3.  It
+        // would be nice to figure out how to avoid this, but it is
+        // difficult because we do need to resize surfaces in some
+        // cases while they are hidden such as when first showing a
+        // window.
+        boolean displayed = false;
+
+        w.computeShownFrameLocked();
+
+        int width, height;
+        if ((w.mAttrs.flags & w.mAttrs.FLAG_SCALED) != 0) {
+            // for a scaled surface, we just want to use
+            // the requested size.
+            width  = w.mRequestedWidth;
+            height = w.mRequestedHeight;
+        } else {
+            width = w.mCompatFrame.width();
+            height = w.mCompatFrame.height();
+        }
+
+        if (width < 1) {
+            width = 1;
+        }
+        if (height < 1) {
+            height = 1;
+        }
+        final boolean surfaceResized = w.mSurfaceW != width || w.mSurfaceH != height;
+        if (surfaceResized) {
+            w.mSurfaceW = width;
+            w.mSurfaceH = height;
+        }
+
+        if (w.mSurfaceX != w.mShownFrame.left
+                || w.mSurfaceY != w.mShownFrame.top) {
+            try {
+                if (SHOW_TRANSACTIONS) logSurface(w,
+                        "POS " + w.mShownFrame.left
+                        + ", " + w.mShownFrame.top, null);
+                w.mSurfaceX = w.mShownFrame.left;
+                w.mSurfaceY = w.mShownFrame.top;
+                w.mSurface.setPosition(w.mShownFrame.left, w.mShownFrame.top);
+            } catch (RuntimeException e) {
+                Slog.w(TAG, "Error positioning surface of " + w
+                        + " pos=(" + w.mShownFrame.left
+                        + "," + w.mShownFrame.top + ")", e);
+                if (!recoveringMemory) {
+                    reclaimSomeSurfaceMemoryLocked(w, "position", true);
+                }
+            }
+        }
+
+        if (surfaceResized) {
+            try {
+                if (SHOW_TRANSACTIONS) logSurface(w,
+                        "SIZE " + width + "x" + height, null);
+                w.mSurfaceResized = true;
+                w.mSurface.setSize(width, height);
+            } catch (RuntimeException e) {
+                // If something goes wrong with the surface (such
+                // as running out of memory), don't take down the
+                // entire system.
+                Slog.e(TAG, "Error resizing surface of " + w
+                        + " size=(" + width + "x" + height + ")", e);
+                if (!recoveringMemory) {
+                    reclaimSomeSurfaceMemoryLocked(w, "size", true);
+                }
+            }
+        }
+
+        if (!w.mAppFreezing && w.mLayoutSeq == mLayoutSeq) {
+            w.mContentInsetsChanged |=
+                !w.mLastContentInsets.equals(w.mContentInsets);
+            w.mVisibleInsetsChanged |=
+                !w.mLastVisibleInsets.equals(w.mVisibleInsets);
+            boolean configChanged =
+                w.mConfiguration != mCurConfiguration
+                && (w.mConfiguration == null
+                        || mCurConfiguration.diff(w.mConfiguration) != 0);
+            if (DEBUG_CONFIGURATION && configChanged) {
+                Slog.v(TAG, "Win " + w + " config changed: "
+                        + mCurConfiguration);
+            }
+            if (localLOGV) Slog.v(TAG, "Resizing " + w
+                    + ": configChanged=" + configChanged
+                    + " last=" + w.mLastFrame + " frame=" + w.mFrame);
+            w.mLastFrame.set(w.mFrame);
+            if (w.mContentInsetsChanged
+                    || w.mVisibleInsetsChanged
+                    || w.mSurfaceResized
+                    || configChanged) {
+                if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
+                    Slog.v(TAG, "Resize reasons: "
+                            + " contentInsetsChanged=" + w.mContentInsetsChanged
+                            + " visibleInsetsChanged=" + w.mVisibleInsetsChanged
+                            + " surfaceResized=" + w.mSurfaceResized
+                            + " configChanged=" + configChanged);
+                }
+
+                w.mLastContentInsets.set(w.mContentInsets);
+                w.mLastVisibleInsets.set(w.mVisibleInsets);
+                makeWindowFreezingScreenIfNeededLocked(w);
+                // If the orientation is changing, then we need to
+                // hold off on unfreezing the display until this
+                // window has been redrawn; to do that, we need
+                // to go through the process of getting informed
+                // by the application when it has finished drawing.
+                if (w.mOrientationChanging) {
+                    if (DEBUG_ORIENTATION) Slog.v(TAG,
+                            "Orientation start waiting for draw in "
+                            + w + ", surface " + w.mSurface);
+                    w.mDrawPending = true;
+                    w.mCommitDrawPending = false;
+                    w.mReadyToShow = false;
+                    if (w.mAppToken != null) {
+                        w.mAppToken.allDrawn = false;
+                    }
+                }
+                if (!mResizingWindows.contains(w)) {
+                    if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG,
+                            "Resizing window " + w + " to " + w.mSurfaceW
+                            + "x" + w.mSurfaceH);
+                    mResizingWindows.add(w);
+                }
+            } else if (w.mOrientationChanging) {
+                if (!w.mDrawPending && !w.mCommitDrawPending) {
+                    if (DEBUG_ORIENTATION) Slog.v(TAG,
+                            "Orientation not waiting for draw in "
+                            + w + ", surface " + w.mSurface);
+                    w.mOrientationChanging = false;
+                }
+            }
+        }
+
+        if (w.mAttachedHidden || !w.isReadyForDisplay()) {
+            if (!w.mLastHidden) {
+                //dump();
+                w.mLastHidden = true;
+                if (SHOW_TRANSACTIONS) logSurface(w,
+                        "HIDE (performLayout)", null);
+                if (w.mSurface != null) {
+                    w.mSurfaceShown = false;
+                    try {
+                        w.mSurface.hide();
+                    } catch (RuntimeException e) {
+                        Slog.w(TAG, "Exception hiding surface in " + w);
+                    }
+                }
+            }
+            // If we are waiting for this window to handle an
+            // orientation change, well, it is hidden, so
+            // doesn't really matter.  Note that this does
+            // introduce a potential glitch if the window
+            // becomes unhidden before it has drawn for the
+            // new orientation.
+            if (w.mOrientationChanging) {
+                w.mOrientationChanging = false;
+                if (DEBUG_ORIENTATION) Slog.v(TAG,
+                        "Orientation change skips hidden " + w);
+            }
+        } else if (w.mLastLayer != w.mAnimLayer
+                || w.mLastAlpha != w.mShownAlpha
+                || w.mLastDsDx != w.mDsDx
+                || w.mLastDtDx != w.mDtDx
+                || w.mLastDsDy != w.mDsDy
+                || w.mLastDtDy != w.mDtDy
+                || w.mLastHScale != w.mHScale
+                || w.mLastVScale != w.mVScale
+                || w.mLastHidden) {
+            displayed = true;
+            w.mLastAlpha = w.mShownAlpha;
+            w.mLastLayer = w.mAnimLayer;
+            w.mLastDsDx = w.mDsDx;
+            w.mLastDtDx = w.mDtDx;
+            w.mLastDsDy = w.mDsDy;
+            w.mLastDtDy = w.mDtDy;
+            w.mLastHScale = w.mHScale;
+            w.mLastVScale = w.mVScale;
+            if (SHOW_TRANSACTIONS) logSurface(w,
+                    "alpha=" + w.mShownAlpha + " layer=" + w.mAnimLayer
+                    + " matrix=[" + (w.mDsDx*w.mHScale)
+                    + "," + (w.mDtDx*w.mVScale)
+                    + "][" + (w.mDsDy*w.mHScale)
+                    + "," + (w.mDtDy*w.mVScale) + "]", null);
+            if (w.mSurface != null) {
+                try {
+                    w.mSurfaceAlpha = w.mShownAlpha;
+                    w.mSurface.setAlpha(w.mShownAlpha);
+                    w.mSurfaceLayer = w.mAnimLayer;
+                    w.mSurface.setLayer(w.mAnimLayer);
+                    w.mSurface.setMatrix(
+                            w.mDsDx*w.mHScale, w.mDtDx*w.mVScale,
+                            w.mDsDy*w.mHScale, w.mDtDy*w.mVScale);
+                } catch (RuntimeException e) {
+                    Slog.w(TAG, "Error updating surface in " + w, e);
+                    if (!recoveringMemory) {
+                        reclaimSomeSurfaceMemoryLocked(w, "update", true);
+                    }
+                }
+            }
+
+            if (w.mLastHidden && !w.mDrawPending
+                    && !w.mCommitDrawPending
+                    && !w.mReadyToShow) {
+                if (SHOW_TRANSACTIONS) logSurface(w,
+                        "SHOW (performLayout)", null);
+                if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + w
+                        + " during relayout");
+                if (showSurfaceRobustlyLocked(w)) {
+                    w.mHasDrawn = true;
+                    w.mLastHidden = false;
+                } else {
+                    w.mOrientationChanging = false;
+                }
+            }
+            if (w.mSurface != null) {
+                w.mToken.hasVisible = true;
+            }
+        } else {
+            displayed = true;
+        }
+
+        if (displayed) {
+            if (w.mOrientationChanging) {
+                if (w.mDrawPending || w.mCommitDrawPending) {
+                    mInnerFields.mOrientationChangeComplete = false;
+                    if (DEBUG_ORIENTATION) Slog.v(TAG,
+                            "Orientation continue waiting for draw in " + w);
+                } else {
+                    w.mOrientationChanging = false;
+                    if (DEBUG_ORIENTATION) Slog.v(TAG,
+                            "Orientation change complete in " + w);
+                }
+            }
+            w.mToken.hasVisible = true;
+        }
+    }
+
+    /**
+     * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
+     *
+     * @param w WindowState this method is applied to.
+     * @param currentTime The time which animations use for calculating transitions.
+     * @param innerDw Width of app window.
+     * @param innerDh Height of app window.
+     */
+    private void handleNotObscuredLocked(final WindowState w, final long currentTime,
+                                         final int innerDw, final int innerDh) {
+        final WindowManager.LayoutParams attrs = w.mAttrs;
+        final int attrFlags = attrs.flags;
+        final boolean canBeSeen = w.isDisplayedLw();
+
+        if (w.mSurface != null) {
+            if ((attrFlags&FLAG_KEEP_SCREEN_ON) != 0) {
+                mInnerFields.mHoldScreen = w.mSession;
+            }
+            if (!mInnerFields.mSyswin && w.mAttrs.screenBrightness >= 0
+                    && mInnerFields.mScreenBrightness < 0) {
+                mInnerFields.mScreenBrightness = w.mAttrs.screenBrightness;
+            }
+            if (!mInnerFields.mSyswin && w.mAttrs.buttonBrightness >= 0
+                    && mInnerFields.mButtonBrightness < 0) {
+                mInnerFields.mButtonBrightness = w.mAttrs.buttonBrightness;
+            }
+            if (canBeSeen
+                    && (attrs.type == WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG
+                     || attrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD
+                     || attrs.type == WindowManager.LayoutParams.TYPE_SYSTEM_ERROR)) {
+                mInnerFields.mSyswin = true;
+            }
+        }
+
+        boolean opaqueDrawn = canBeSeen && w.isOpaqueDrawn();
+        if (opaqueDrawn && w.isFullscreen(innerDw, innerDh)) {
+            // This window completely covers everything behind it,
+            // so we want to leave all of them as unblurred (for
+            // performance reasons).
+            mInnerFields.mObscured = true;
+        } else if (canBeSeen && (attrFlags & FLAG_BLUR_BEHIND | FLAG_DIM_BEHIND) != 0) {
+            if (localLOGV) Slog.v(TAG, "Win " + w
+                    + ": blurring=" + mInnerFields.mBlurring
+                    + " obscured=" + mInnerFields.mObscured);
+            if ((attrFlags&FLAG_DIM_BEHIND) != 0) {
+                if (!mInnerFields.mDimming) {
+                    //Slog.i(TAG, "DIM BEHIND: " + w);
+                    mInnerFields.mDimming = true;
+                    if (mDimAnimator == null) {
+                        mDimAnimator = new DimAnimator(mFxSession);
+                    }
+                    mDimAnimator.show(innerDw, innerDh);
+                    mDimAnimator.updateParameters(mContext.getResources(),
+                            w, currentTime);
+                }
+            }
+            if ((attrFlags & FLAG_BLUR_BEHIND) != 0) {
+                if (!mInnerFields.mBlurring) {
+                    //Slog.i(TAG, "BLUR BEHIND: " + w);
+                    mInnerFields.mBlurring = true;
+                    if (mBlurSurface == null) {
+                        try {
+                            mBlurSurface = new Surface(mFxSession, 0,
+                                    "BlurSurface",
+                                    -1, 16, 16,
+                                    PixelFormat.OPAQUE,
+                                    Surface.FX_SURFACE_BLUR);
+                        } catch (Exception e) {
+                            Slog.e(TAG, "Exception creating Blur surface", e);
+                        }
+                        if (SHOW_TRANSACTIONS) Slog.i(TAG, "  BLUR "
+                                + mBlurSurface + ": CREATE");
+                    }
+                    final int dw = mCurDisplayWidth;
+                    final int dh = mCurDisplayHeight;
+                    if (mBlurSurface != null) {
+                        if (SHOW_TRANSACTIONS) Slog.i(TAG, "  BLUR "
+                                + mBlurSurface + ": pos=(0,0) (" +
+                                dw + "x" + dh + "), layer=" + (w.mAnimLayer-1));
+                        mBlurSurface.setPosition(0, 0);
+                        mBlurSurface.setSize(dw, dh);
+                        mBlurSurface.setLayer(w.mAnimLayer-LAYER_OFFSET_BLUR);
+                        if (!mBlurShown) {
+                            try {
+                                if (SHOW_TRANSACTIONS) Slog.i(TAG, "  BLUR "
+                                        + mBlurSurface + ": SHOW");
+                                mBlurSurface.show();
+                            } catch (RuntimeException e) {
+                                Slog.w(TAG, "Failure showing blur surface", e);
+                            }
+                            mBlurShown = true;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     // "Something has changed!  Let's make it correct now."
     private final void performLayoutAndPlaceSurfacesLockedInner(
             boolean recoveringMemory) {
@@ -7598,15 +8628,14 @@
             mExitingAppTokens.get(i).hasVisible = false;
         }
 
-        boolean orientationChangeComplete = true;
-        Session holdScreen = null;
-        float screenBrightness = -1;
-        float buttonBrightness = -1;
+        mInnerFields.mOrientationChangeComplete = true;
+        mInnerFields.mHoldScreen = null;
+        mInnerFields.mScreenBrightness = -1;
+        mInnerFields.mButtonBrightness = -1;
         boolean focusDisplayed = false;
-        boolean animating = false;
+        mInnerFields.mAnimating = false;
         boolean createWatermark = false;
         boolean updateRotation = false;
-        boolean screenRotationFinished = false;
 
         if (mFxSession == null) {
             mFxSession = new SurfaceSession();
@@ -7629,7 +8658,7 @@
         }
 
         try {
-            boolean wallpaperForceHidingChanged = false;
+            mInnerFields.mWallpaperForceHidingChanged = false;
             int repeats = 0;
             int changes = 0;
             
@@ -7640,655 +8669,93 @@
                     mLayoutNeeded = false;
                     break;
                 }
-                
-                if ((changes&(WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER
-                        | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG
-                        | WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT)) != 0) {
-                    if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
-                        if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
-                            assignLayersLocked();
-                            mLayoutNeeded = true;
-                        }
-                    }
-                    if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
-                        if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
-                        if (updateOrientationFromAppTokensLocked(true)) {
-                            mLayoutNeeded = true;
-                            mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
-                        }
-                    }
-                    if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+
+                if ((changes & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+                    if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
+                        assignLayersLocked();
                         mLayoutNeeded = true;
                     }
                 }
-                
+                if ((changes & WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
+                    if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
+                    if (updateOrientationFromAppTokensLocked(true)) {
+                        mLayoutNeeded = true;
+                        mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
+                    }
+                }
+                if ((changes & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+                    mLayoutNeeded = true;
+                }
+
                 // FIRST LOOP: Perform a layout, if needed.
                 if (repeats < 4) {
-                    changes = performLayoutLockedInner(repeats == 0, false /*updateInputWindows*/);
-                    if (changes != 0) {
-                        continue;
-                    }
+                    performLayoutLockedInner(repeats == 0, false /*updateInputWindows*/);
                 } else {
                     Slog.w(TAG, "Layout repeat skipped after too many iterations");
-                    changes = 0;
                 }
-                
-                final int transactionSequence = ++mTransactionSequence;
+
+                changes = 0;
+                ++mTransactionSequence;
 
                 // Update animations of all applications, including those
                 // associated with exiting/removed apps
-                boolean tokensAnimating = false;
-                final int NAT = mAppTokens.size();
-                for (i=0; i<NAT; i++) {
-                    if (mAppTokens.get(i).stepAnimationLocked(currentTime,
-                            innerDw, innerDh)) {
-                        tokensAnimating = true;
-                    }
-                }
-                final int NEAT = mExitingAppTokens.size();
-                for (i=0; i<NEAT; i++) {
-                    if (mExitingAppTokens.get(i).stepAnimationLocked(currentTime,
-                            innerDw, innerDh)) {
-                        tokensAnimating = true;
-                    }
-                }
+                mInnerFields.mAnimating = false;
 
                 // SECOND LOOP: Execute animations and update visibility of windows.
-                
+                updateRotation =
+                        updateAppsAndRotationAnimationsLocked(currentTime, innerDw, innerDh);
+
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: seq="
-                        + transactionSequence + " tokensAnimating="
-                        + tokensAnimating);
-                        
-                animating = tokensAnimating;
+                        + mTransactionSequence + " mAnimating="
+                        + mInnerFields.mAnimating);
 
-                if (mScreenRotationAnimation != null) {
-                    if (mScreenRotationAnimation.isAnimating()) {
-                        if (mScreenRotationAnimation.stepAnimation(currentTime)) {
-                            animating = true;
-                        } else {
-                            screenRotationFinished = true;
-                            updateRotation = true;
-                        }
-                    }
-                }
-
-                boolean tokenMayBeDrawn = false;
-                boolean wallpaperMayChange = false;
-                boolean forceHiding = false;
-                WindowState windowDetachedWallpaper = null;
-                WindowState windowAnimationBackground = null;
-                int windowAnimationBackgroundColor = 0;
+                mInnerFields.mTokenMayBeDrawn = false;
+                mInnerFields.mWallpaperMayChange = false;
+                mInnerFields.mForceHiding = false;
+                mInnerFields.mDetachedWallpaper = null;
+                mInnerFields.mWindowAnimationBackground = null;
+                mInnerFields.mWindowAnimationBackgroundColor = 0;
 
                 mPolicy.beginAnimationLw(dw, dh);
 
-                final int N = mWindows.size();
-
-                for (i=N-1; i>=0; i--) {
-                    WindowState w = mWindows.get(i);
-
-                    final WindowManager.LayoutParams attrs = w.mAttrs;
-
-                    if (w.mSurface != null) {
-                        // Take care of the window being ready to display.
-                        if (w.commitFinishDrawingLocked(currentTime)) {
-                            if ((w.mAttrs.flags
-                                    & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
-                                if (DEBUG_WALLPAPER) Slog.v(TAG,
-                                        "First draw done in potential wallpaper target " + w);
-                                wallpaperMayChange = true;
-                            }
-                        }
-
-                        final boolean wasAnimating = w.mAnimating;
-
-                        int animDw = innerDw;
-                        int animDh = innerDh;
-
-                        // If the window has moved due to its containing
-                        // content frame changing, then we'd like to animate
-                        // it.  The checks here are ordered by what is least
-                        // likely to be true first.
-                        if (w.shouldAnimateMove()) {
-                            // Frame has moved, containing content frame
-                            // has also moved, and we're not currently animating...
-                            // let's do something.
-                            Animation a = AnimationUtils.loadAnimation(mContext,
-                                    com.android.internal.R.anim.window_move_from_decor);
-                            w.setAnimation(a);
-                            animDw = w.mLastFrame.left - w.mFrame.left;
-                            animDh = w.mLastFrame.top - w.mFrame.top;
-                        }
-
-                        // Execute animation.
-                        final boolean nowAnimating = w.stepAnimationLocked(currentTime,
-                                animDw, animDh);
-
-                        // If this window is animating, make a note that we have
-                        // an animating window and take care of a request to run
-                        // a detached wallpaper animation.
-                        if (nowAnimating) {
-                            if (w.mAnimation != null) {
-                                if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0
-                                        && w.mAnimation.getDetachWallpaper()) {
-                                    windowDetachedWallpaper = w;
-                                }
-                                if (w.mAnimation.getBackgroundColor() != 0) {
-                                    if (windowAnimationBackground == null || w.mAnimLayer <
-                                            windowAnimationBackground.mAnimLayer) {
-                                        windowAnimationBackground = w;
-                                        windowAnimationBackgroundColor =
-                                                w.mAnimation.getBackgroundColor();
-                                    }
-                                }
-                            }
-                            animating = true;
-                        }
-
-                        // If this window's app token is running a detached wallpaper
-                        // animation, make a note so we can ensure the wallpaper is
-                        // displayed behind it.
-                        if (w.mAppToken != null && w.mAppToken.animation != null
-                                && w.mAppToken.animating) {
-                            if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0
-                                    && w.mAppToken.animation.getDetachWallpaper()) {
-                                windowDetachedWallpaper = w;
-                            }
-                            if (w.mAppToken.animation.getBackgroundColor() != 0) {
-                                if (windowAnimationBackground == null || w.mAnimLayer <
-                                        windowAnimationBackground.mAnimLayer) {
-                                    windowAnimationBackground = w;
-                                    windowAnimationBackgroundColor =
-                                            w.mAppToken.animation.getBackgroundColor();
-                                }
-                            }
-                        }
-
-                        if (wasAnimating && !w.mAnimating && mWallpaperTarget == w) {
-                            wallpaperMayChange = true;
-                        }
-
-                        if (mPolicy.doesForceHide(w, attrs)) {
-                            if (!wasAnimating && nowAnimating) {
-                                if (DEBUG_VISIBILITY) Slog.v(TAG,
-                                        "Animation started that could impact force hide: "
-                                        + w);
-                                wallpaperForceHidingChanged = true;
-                                mFocusMayChange = true;
-                            } else if (w.isReadyForDisplay() && w.mAnimation == null) {
-                                forceHiding = true;
-                            }
-                        } else if (mPolicy.canBeForceHidden(w, attrs)) {
-                            boolean changed;
-                            if (forceHiding) {
-                                changed = w.hideLw(false, false);
-                                if (DEBUG_VISIBILITY && changed) Slog.v(TAG,
-                                        "Now policy hidden: " + w);
-                            } else {
-                                changed = w.showLw(false, false);
-                                if (DEBUG_VISIBILITY && changed) Slog.v(TAG,
-                                        "Now policy shown: " + w);
-                                if (changed) {
-                                    if (wallpaperForceHidingChanged
-                                            && w.isVisibleNow() /*w.isReadyForDisplay()*/) {
-                                        // Assume we will need to animate.  If
-                                        // we don't (because the wallpaper will
-                                        // stay with the lock screen), then we will
-                                        // clean up later.
-                                        Animation a = mPolicy.createForceHideEnterAnimation();
-                                        if (a != null) {
-                                            w.setAnimation(a);
-                                        }
-                                    }
-                                    if (mCurrentFocus == null ||
-                                            mCurrentFocus.mLayer < w.mLayer) {
-                                        // We are showing on to of the current
-                                        // focus, so re-evaluate focus to make
-                                        // sure it is correct.
-                                        mFocusMayChange = true;
-                                    }
-                                }
-                            }
-                            if (changed && (attrs.flags
-                                    & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
-                                wallpaperMayChange = true;
-                            }
-                        }
-
-                        mPolicy.animatingWindowLw(w, attrs);
-                    }
-
-                    final AppWindowToken atoken = w.mAppToken;
-                    if (atoken != null && (!atoken.allDrawn || atoken.freezingScreen)) {
-                        if (atoken.lastTransactionSequence != transactionSequence) {
-                            atoken.lastTransactionSequence = transactionSequence;
-                            atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
-                            atoken.startingDisplayed = false;
-                        }
-                        if ((w.isOnScreen() || w.mAttrs.type
-                                == WindowManager.LayoutParams.TYPE_BASE_APPLICATION)
-                                && !w.mExiting && !w.mDestroying) {
-                            if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) {
-                                Slog.v(TAG, "Eval win " + w + ": isDrawn="
-                                        + w.isDrawnLw()
-                                        + ", isAnimating=" + w.isAnimating());
-                                if (!w.isDrawnLw()) {
-                                    Slog.v(TAG, "Not displayed: s=" + w.mSurface
-                                            + " pv=" + w.mPolicyVisibility
-                                            + " dp=" + w.mDrawPending
-                                            + " cdp=" + w.mCommitDrawPending
-                                            + " ah=" + w.mAttachedHidden
-                                            + " th=" + atoken.hiddenRequested
-                                            + " a=" + w.mAnimating);
-                                }
-                            }
-                            if (w != atoken.startingWindow) {
-                                if (!atoken.freezingScreen || !w.mAppFreezing) {
-                                    atoken.numInterestingWindows++;
-                                    if (w.isDrawnLw()) {
-                                        atoken.numDrawnWindows++;
-                                        if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) Slog.v(TAG,
-                                                "tokenMayBeDrawn: " + atoken
-                                                + " freezingScreen=" + atoken.freezingScreen
-                                                + " mAppFreezing=" + w.mAppFreezing);
-                                        tokenMayBeDrawn = true;
-                                    }
-                                }
-                            } else if (w.isDrawnLw()) {
-                                atoken.startingDisplayed = true;
-                            }
-                        }
-                    } else if (w.mReadyToShow) {
-                        w.performShowLocked();
-                    }
-                }
+                updateWindowsAndWallpaperLocked(currentTime, innerDw, innerDh);
 
                 changes |= mPolicy.finishAnimationLw();
 
-                if (tokenMayBeDrawn) {
-                    // See if any windows have been drawn, so they (and others
-                    // associated with them) can now be shown.
-                    final int NT = mAppTokens.size();
-                    for (i=0; i<NT; i++) {
-                        AppWindowToken wtoken = mAppTokens.get(i);
-                        if (wtoken.freezingScreen) {
-                            int numInteresting = wtoken.numInterestingWindows;
-                            if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
-                                if (DEBUG_VISIBILITY) Slog.v(TAG,
-                                        "allDrawn: " + wtoken
-                                        + " interesting=" + numInteresting
-                                        + " drawn=" + wtoken.numDrawnWindows);
-                                wtoken.showAllWindowsLocked();
-                                unsetAppFreezingScreenLocked(wtoken, false, true);
-                                if (DEBUG_ORIENTATION) Slog.i(TAG,
-                                        "Setting orientationChangeComplete=true because wtoken "
-                                        + wtoken + " numInteresting=" + numInteresting
-                                        + " numDrawn=" + wtoken.numDrawnWindows);
-                                orientationChangeComplete = true;
-                            }
-                        } else if (!wtoken.allDrawn) {
-                            int numInteresting = wtoken.numInterestingWindows;
-                            if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
-                                if (DEBUG_VISIBILITY) Slog.v(TAG,
-                                        "allDrawn: " + wtoken
-                                        + " interesting=" + numInteresting
-                                        + " drawn=" + wtoken.numDrawnWindows);
-                                wtoken.allDrawn = true;
-                                changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
-
-                                // We can now show all of the drawn windows!
-                                if (!mOpeningApps.contains(wtoken)) {
-                                    wtoken.showAllWindowsLocked();
-                                }
-                            }
-                        }
-                    }
+                if (mInnerFields.mTokenMayBeDrawn) {
+                    changes |= testTokenMayBeDrawnLocked();
                 }
 
                 // If we are ready to perform an app transition, check through
                 // all of the app tokens to be shown and see if they are ready
                 // to go.
                 if (mAppTransitionReady) {
-                    int NN = mOpeningApps.size();
-                    boolean goodToGo = true;
-                    if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
-                            "Checking " + NN + " opening apps (frozen="
-                            + mDisplayFrozen + " timeout="
-                            + mAppTransitionTimeout + ")...");
-                    if (!mDisplayFrozen && !mAppTransitionTimeout) {
-                        // If the display isn't frozen, wait to do anything until
-                        // all of the apps are ready.  Otherwise just go because
-                        // we'll unfreeze the display when everyone is ready.
-                        for (i=0; i<NN && goodToGo; i++) {
-                            AppWindowToken wtoken = mOpeningApps.get(i);
-                            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
-                                    "Check opening app" + wtoken + ": allDrawn="
-                                    + wtoken.allDrawn + " startingDisplayed="
-                                    + wtoken.startingDisplayed);
-                            if (!wtoken.allDrawn && !wtoken.startingDisplayed
-                                    && !wtoken.startingMoved) {
-                                goodToGo = false;
-                            }
-                        }
-                    }
-                    if (goodToGo) {
-                        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
-                        int transit = mNextAppTransition;
-                        if (mSkipAppTransitionAnimation) {
-                            transit = WindowManagerPolicy.TRANSIT_UNSET;
-                        }
-                        mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
-                        mAppTransitionReady = false;
-                        mAppTransitionRunning = true;
-                        mAppTransitionTimeout = false;
-                        mStartingIconInTransition = false;
-                        mSkipAppTransitionAnimation = false;
-
-                        mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
-
-                        // If there are applications waiting to come to the
-                        // top of the stack, now is the time to move their windows.
-                        // (Note that we don't do apps going to the bottom
-                        // here -- we want to keep their windows in the old
-                        // Z-order until the animation completes.)
-                        if (mToTopApps.size() > 0) {
-                            NN = mAppTokens.size();
-                            for (i=0; i<NN; i++) {
-                                AppWindowToken wtoken = mAppTokens.get(i);
-                                if (wtoken.sendingToTop) {
-                                    wtoken.sendingToTop = false;
-                                    moveAppWindowsLocked(wtoken, NN, false);
-                                }
-                            }
-                            mToTopApps.clear();
-                        }
-
-                        WindowState oldWallpaper = mWallpaperTarget;
-
-                        adjustWallpaperWindowsLocked();
-                        wallpaperMayChange = false;
-
-                        // The top-most window will supply the layout params,
-                        // and we will determine it below.
-                        LayoutParams animLp = null;
-                        int bestAnimLayer = -1;
-                        boolean fullscreenAnim = false;
-
-                        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
-                                "New wallpaper target=" + mWallpaperTarget
-                                + ", lower target=" + mLowerWallpaperTarget
-                                + ", upper target=" + mUpperWallpaperTarget);
-                        int foundWallpapers = 0;
-                        // Do a first pass through the tokens for two
-                        // things:
-                        // (1) Determine if both the closing and opening
-                        // app token sets are wallpaper targets, in which
-                        // case special animations are needed
-                        // (since the wallpaper needs to stay static
-                        // behind them).
-                        // (2) Find the layout params of the top-most
-                        // application window in the tokens, which is
-                        // what will control the animation theme.
-                        final int NC = mClosingApps.size();
-                        NN = NC + mOpeningApps.size();
-                        for (i=0; i<NN; i++) {
-                            AppWindowToken wtoken;
-                            int mode;
-                            if (i < NC) {
-                                wtoken = mClosingApps.get(i);
-                                mode = 1;
-                            } else {
-                                wtoken = mOpeningApps.get(i-NC);
-                                mode = 2;
-                            }
-                            if (mLowerWallpaperTarget != null) {
-                                if (mLowerWallpaperTarget.mAppToken == wtoken
-                                        || mUpperWallpaperTarget.mAppToken == wtoken) {
-                                    foundWallpapers |= mode;
-                                }
-                            }
-                            if (wtoken.appFullscreen) {
-                                WindowState ws = wtoken.findMainWindow();
-                                if (ws != null) {
-                                    animLp = ws.mAttrs;
-                                    bestAnimLayer = ws.mLayer;
-                                    fullscreenAnim = true;
-                                }
-                            } else if (!fullscreenAnim) {
-                                WindowState ws = wtoken.findMainWindow();
-                                if (ws != null) {
-                                    if (ws.mLayer > bestAnimLayer) {
-                                        animLp = ws.mAttrs;
-                                        bestAnimLayer = ws.mLayer;
-                                    }
-                                }
-                            }
-                        }
-
-                        if (foundWallpapers == 3) {
-                            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
-                                    "Wallpaper animation!");
-                            switch (transit) {
-                                case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
-                                case WindowManagerPolicy.TRANSIT_TASK_OPEN:
-                                case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT:
-                                    transit = WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN;
-                                    break;
-                                case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE:
-                                case WindowManagerPolicy.TRANSIT_TASK_CLOSE:
-                                case WindowManagerPolicy.TRANSIT_TASK_TO_BACK:
-                                    transit = WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_CLOSE;
-                                    break;
-                            }
-                            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
-                                    "New transit: " + transit);
-                        } else if (oldWallpaper != null) {
-                            // We are transitioning from an activity with
-                            // a wallpaper to one without.
-                            transit = WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE;
-                            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
-                                    "New transit away from wallpaper: " + transit);
-                        } else if (mWallpaperTarget != null) {
-                            // We are transitioning from an activity without
-                            // a wallpaper to now showing the wallpaper
-                            transit = WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN;
-                            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
-                                    "New transit into wallpaper: " + transit);
-                        }
-
-                        // If all closing windows are obscured, then there is
-                        // no need to do an animation.  This is the case, for
-                        // example, when this transition is being done behind
-                        // the lock screen.
-                        if (!mPolicy.allowAppAnimationsLw()) {
-                            animLp = null;
-                        }
-                        
-                        NN = mOpeningApps.size();
-                        for (i=0; i<NN; i++) {
-                            AppWindowToken wtoken = mOpeningApps.get(i);
-                            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
-                                    "Now opening app" + wtoken);
-                            wtoken.reportedVisible = false;
-                            wtoken.inPendingTransaction = false;
-                            wtoken.animation = null;
-                            setTokenVisibilityLocked(wtoken, animLp, true,
-                                    transit, false);
-                            wtoken.updateReportedVisibilityLocked();
-                            wtoken.waitingToShow = false;
-                            wtoken.showAllWindowsLocked();
-                        }
-                        NN = mClosingApps.size();
-                        for (i=0; i<NN; i++) {
-                            AppWindowToken wtoken = mClosingApps.get(i);
-                            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
-                                    "Now closing app" + wtoken);
-                            wtoken.inPendingTransaction = false;
-                            wtoken.animation = null;
-                            setTokenVisibilityLocked(wtoken, animLp, false,
-                                    transit, false);
-                            wtoken.updateReportedVisibilityLocked();
-                            wtoken.waitingToHide = false;
-                            // Force the allDrawn flag, because we want to start
-                            // this guy's animations regardless of whether it's
-                            // gotten drawn.
-                            wtoken.allDrawn = true;
-                        }
-
-                        mNextAppTransitionPackage = null;
-
-                        mOpeningApps.clear();
-                        mClosingApps.clear();
-
-                        // This has changed the visibility of windows, so perform
-                        // a new layout to get them all up-to-date.
-                        changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT
-                                | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
-                        mLayoutNeeded = true;
-                        if (!moveInputMethodWindowsIfNeededLocked(true)) {
-                            assignLayersLocked();
-                        }
-                        updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
-                                false /*updateInputWindows*/);
-                        mFocusMayChange = false;
-                    }
+                    changes |= handleAppTransitionReadyLocked();
                 }
 
-                int adjResult = 0;
+                mInnerFields.mAdjResult = 0;
 
-                if (!animating && mAppTransitionRunning) {
+                if (!mInnerFields.mAnimating && mAppTransitionRunning) {
                     // We have finished the animation of an app transition.  To do
                     // this, we have delayed a lot of operations like showing and
                     // hiding apps, moving apps in Z-order, etc.  The app token list
                     // reflects the correct Z-order, but the window list may now
                     // be out of sync with it.  So here we will just rebuild the
                     // entire app window list.  Fun!
-                    mAppTransitionRunning = false;
-                    // Clear information about apps that were moving.
-                    mToBottomApps.clear();
-
-                    rebuildAppWindowListLocked();
-                    changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
-                    adjResult |= ADJUST_WALLPAPER_LAYERS_CHANGED;
-                    moveInputMethodWindowsIfNeededLocked(false);
-                    wallpaperMayChange = true;
-                    // Since the window list has been rebuilt, focus might
-                    // have to be recomputed since the actual order of windows
-                    // might have changed again.
-                    mFocusMayChange = true;
+                    changes |= handleAnimatingAndTransitionLocked();
                 }
 
-                if (wallpaperForceHidingChanged && changes == 0 && !mAppTransitionReady) {
+                if (mInnerFields.mWallpaperForceHidingChanged && changes == 0 && !mAppTransitionReady) {
                     // At this point, there was a window with a wallpaper that
                     // was force hiding other windows behind it, but now it
                     // is going away.  This may be simple -- just animate
                     // away the wallpaper and its window -- or it may be
                     // hard -- the wallpaper now needs to be shown behind
                     // something that was hidden.
-                    WindowState oldWallpaper = mWallpaperTarget;
-                    if (mLowerWallpaperTarget != null
-                            && mLowerWallpaperTarget.mAppToken != null) {
-                        if (DEBUG_WALLPAPER) Slog.v(TAG,
-                                "wallpaperForceHiding changed with lower="
-                                + mLowerWallpaperTarget);
-                        if (DEBUG_WALLPAPER) Slog.v(TAG,
-                                "hidden=" + mLowerWallpaperTarget.mAppToken.hidden +
-                                " hiddenRequested=" + mLowerWallpaperTarget.mAppToken.hiddenRequested);
-                        if (mLowerWallpaperTarget.mAppToken.hidden) {
-                            // The lower target has become hidden before we
-                            // actually started the animation...  let's completely
-                            // re-evaluate everything.
-                            mLowerWallpaperTarget = mUpperWallpaperTarget = null;
-                            changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
-                        }
-                    }
-                    adjResult |= adjustWallpaperWindowsLocked();
-                    wallpaperMayChange = false;
-                    wallpaperForceHidingChanged = false;
-                    if (DEBUG_WALLPAPER) Slog.v(TAG, "****** OLD: " + oldWallpaper
-                            + " NEW: " + mWallpaperTarget
-                            + " LOWER: " + mLowerWallpaperTarget);
-                    if (mLowerWallpaperTarget == null) {
-                        // Whoops, we don't need a special wallpaper animation.
-                        // Clear them out.
-                        forceHiding = false;
-                        for (i=N-1; i>=0; i--) {
-                            WindowState w = mWindows.get(i);
-                            if (w.mSurface != null) {
-                                final WindowManager.LayoutParams attrs = w.mAttrs;
-                                if (mPolicy.doesForceHide(w, attrs) && w.isVisibleLw()) {
-                                    if (DEBUG_FOCUS) Slog.i(TAG, "win=" + w + " force hides other windows");
-                                    forceHiding = true;
-                                } else if (mPolicy.canBeForceHidden(w, attrs)) {
-                                    if (!w.mAnimating) {
-                                        // We set the animation above so it
-                                        // is not yet running.
-                                        w.clearAnimation();
-                                    }
-                                }
-                            }
-                        }
-                    }
+                    changes |= animateAwayWallpaperLocked();
                 }
 
-                if (mWindowDetachedWallpaper != windowDetachedWallpaper) {
-                    if (DEBUG_WALLPAPER) Slog.v(TAG,
-                            "Detached wallpaper changed from " + mWindowDetachedWallpaper
-                            + " to " + windowDetachedWallpaper);
-                    mWindowDetachedWallpaper = windowDetachedWallpaper;
-                    wallpaperMayChange = true;
-                }
-
-                if (windowAnimationBackgroundColor != 0) {
-                    // If the window that wants black is the current wallpaper
-                    // target, then the black goes *below* the wallpaper so we
-                    // don't cause the wallpaper to suddenly disappear.
-                    WindowState target = windowAnimationBackground;
-                    if (mWallpaperTarget == windowAnimationBackground
-                            || mLowerWallpaperTarget == windowAnimationBackground
-                            || mUpperWallpaperTarget == windowAnimationBackground) {
-                        for (i=0; i<mWindows.size(); i++) {
-                            WindowState w = mWindows.get(i);
-                            if (w.mIsWallpaper) {
-                                target = w;
-                                break;
-                            }
-                        }
-                    }
-                    if (mWindowAnimationBackgroundSurface == null) {
-                        mWindowAnimationBackgroundSurface = new DimSurface(mFxSession);
-                    }
-                    mWindowAnimationBackgroundSurface.show(dw, dh,
-                            target.mAnimLayer - LAYER_OFFSET_DIM,
-                            windowAnimationBackgroundColor);
-                } else if (mWindowAnimationBackgroundSurface != null) {
-                    mWindowAnimationBackgroundSurface.hide();
-                }
-
-                if (wallpaperMayChange) {
-                    if (DEBUG_WALLPAPER) Slog.v(TAG,
-                            "Wallpaper may change!  Adjusting");
-                    adjResult |= adjustWallpaperWindowsLocked();
-                }
-
-                if ((adjResult&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
-                    if (DEBUG_WALLPAPER) Slog.v(TAG,
-                            "Wallpaper layer changed: assigning layers + relayout");
-                    changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
-                    assignLayersLocked();
-                } else if ((adjResult&ADJUST_WALLPAPER_VISIBILITY_CHANGED) != 0) {
-                    if (DEBUG_WALLPAPER) Slog.v(TAG,
-                            "Wallpaper visibility changed: relayout");
-                    changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
-                }
-
-                if (mFocusMayChange) {
-                    mFocusMayChange = false;
-                    if (updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
-                            false /*updateInputWindows*/)) {
-                        changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
-                        adjResult = 0;
-                    }
-                }
+                changes |= testWallpaperAndBackgroundLocked();
 
                 if (mLayoutNeeded) {
                     changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
@@ -8302,278 +8769,22 @@
 
             final boolean someoneLosingFocus = mLosingFocus.size() != 0;
 
-            boolean obscured = false;
-            boolean blurring = false;
-            boolean dimming = false;
-            boolean covered = false;
-            boolean syswin = false;
+            mInnerFields.mObscured = false;
+            mInnerFields.mBlurring = false;
+            mInnerFields.mDimming = false;
+            mInnerFields.mSyswin = false;
 
             final int N = mWindows.size();
 
             for (i=N-1; i>=0; i--) {
                 WindowState w = mWindows.get(i);
 
-                boolean displayed = false;
-                final WindowManager.LayoutParams attrs = w.mAttrs;
-                final int attrFlags = attrs.flags;
-
                 if (w.mSurface != null) {
-                    // XXX NOTE: The logic here could be improved.  We have
-                    // the decision about whether to resize a window separated
-                    // from whether to hide the surface.  This can cause us to
-                    // resize a surface even if we are going to hide it.  You
-                    // can see this by (1) holding device in landscape mode on
-                    // home screen; (2) tapping browser icon (device will rotate
-                    // to landscape; (3) tap home.  The wallpaper will be resized
-                    // in step 2 but then immediately hidden, causing us to
-                    // have to resize and then redraw it again in step 3.  It
-                    // would be nice to figure out how to avoid this, but it is
-                    // difficult because we do need to resize surfaces in some
-                    // cases while they are hidden such as when first showing a
-                    // window.
-                    
-                    w.computeShownFrameLocked();
-                    if (localLOGV) Slog.v(
-                            TAG, "Placing surface #" + i + " " + w.mSurface
-                            + ": new=" + w.mShownFrame);
-
-                    if (w.mSurface != null) {
-                        int width, height;
-                        if ((w.mAttrs.flags & w.mAttrs.FLAG_SCALED) != 0) {
-                            // for a scaled surface, we just want to use
-                            // the requested size.
-                            width  = w.mRequestedWidth;
-                            height = w.mRequestedHeight;
-                        } else {
-                            width = w.mCompatFrame.width();
-                            height = w.mCompatFrame.height();
-                        }
-
-                        if (width < 1) {
-                            width = 1;
-                        }
-                        if (height < 1) {
-                            height = 1;
-                        }
-                        final boolean surfaceResized = w.mSurfaceW != width || w.mSurfaceH != height;
-                        if (surfaceResized) {
-                            w.mSurfaceW = width;
-                            w.mSurfaceH = height;
-                        }
-
-                        if (w.mSurfaceX != w.mShownFrame.left
-                                || w.mSurfaceY != w.mShownFrame.top) {
-                            try {
-                                if (SHOW_TRANSACTIONS) logSurface(w,
-                                        "POS " + w.mShownFrame.left
-                                        + ", " + w.mShownFrame.top, null);
-                                w.mSurfaceX = w.mShownFrame.left;
-                                w.mSurfaceY = w.mShownFrame.top;
-                                w.mSurface.setPosition(w.mShownFrame.left, w.mShownFrame.top);
-                            } catch (RuntimeException e) {
-                                Slog.w(TAG, "Error positioning surface of " + w
-                                        + " pos=(" + w.mShownFrame.left
-                                        + "," + w.mShownFrame.top + ")", e);
-                                if (!recoveringMemory) {
-                                    reclaimSomeSurfaceMemoryLocked(w, "position", true);
-                                }
-                            }
-                        }
-
-                        if (surfaceResized) {
-                            try {
-                                if (SHOW_TRANSACTIONS) logSurface(w,
-                                        "SIZE " + width + "x" + height, null);
-                                w.mSurfaceResized = true;
-                                w.mSurface.setSize(width, height);
-                            } catch (RuntimeException e) {
-                                // If something goes wrong with the surface (such
-                                // as running out of memory), don't take down the
-                                // entire system.
-                                Slog.e(TAG, "Error resizing surface of " + w
-                                        + " size=(" + width + "x" + height + ")", e);
-                                if (!recoveringMemory) {
-                                    reclaimSomeSurfaceMemoryLocked(w, "size", true);
-                                }
-                            }
-                        }
-                    }
-
-                    if (!w.mAppFreezing && w.mLayoutSeq == mLayoutSeq) {
-                        w.mContentInsetsChanged |=
-                            !w.mLastContentInsets.equals(w.mContentInsets);
-                        w.mVisibleInsetsChanged |=
-                            !w.mLastVisibleInsets.equals(w.mVisibleInsets);
-                        boolean configChanged =
-                            w.mConfiguration != mCurConfiguration
-                            && (w.mConfiguration == null
-                                    || mCurConfiguration.diff(w.mConfiguration) != 0);
-                        if (DEBUG_CONFIGURATION && configChanged) {
-                            Slog.v(TAG, "Win " + w + " config changed: "
-                                    + mCurConfiguration);
-                        }
-                        if (localLOGV) Slog.v(TAG, "Resizing " + w
-                                + ": configChanged=" + configChanged
-                                + " last=" + w.mLastFrame + " frame=" + w.mFrame);
-                        w.mLastFrame.set(w.mFrame);
-                        if (w.mContentInsetsChanged
-                                || w.mVisibleInsetsChanged
-                                || w.mSurfaceResized
-                                || configChanged) {
-                            if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
-                                Slog.v(TAG, "Resize reasons: "
-                                        + " contentInsetsChanged=" + w.mContentInsetsChanged
-                                        + " visibleInsetsChanged=" + w.mVisibleInsetsChanged
-                                        + " surfaceResized=" + w.mSurfaceResized
-                                        + " configChanged=" + configChanged);
-                            }
-
-                            w.mLastContentInsets.set(w.mContentInsets);
-                            w.mLastVisibleInsets.set(w.mVisibleInsets);
-                            makeWindowFreezingScreenIfNeededLocked(w);
-                            // If the orientation is changing, then we need to
-                            // hold off on unfreezing the display until this
-                            // window has been redrawn; to do that, we need
-                            // to go through the process of getting informed
-                            // by the application when it has finished drawing.
-                            if (w.mOrientationChanging) {
-                                if (DEBUG_ORIENTATION) Slog.v(TAG,
-                                        "Orientation start waiting for draw in "
-                                        + w + ", surface " + w.mSurface);
-                                w.mDrawPending = true;
-                                w.mCommitDrawPending = false;
-                                w.mReadyToShow = false;
-                                if (w.mAppToken != null) {
-                                    w.mAppToken.allDrawn = false;
-                                }
-                            }
-                            if (!mResizingWindows.contains(w)) {
-                                if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG,
-                                        "Resizing window " + w + " to " + w.mSurfaceW
-                                        + "x" + w.mSurfaceH);
-                                mResizingWindows.add(w);
-                            }
-                        } else if (w.mOrientationChanging) {
-                            if (!w.mDrawPending && !w.mCommitDrawPending) {
-                                if (DEBUG_ORIENTATION) Slog.v(TAG,
-                                        "Orientation not waiting for draw in "
-                                        + w + ", surface " + w.mSurface);
-                                w.mOrientationChanging = false;
-                            }
-                        }
-                    }
-
-                    if (w.mAttachedHidden || !w.isReadyForDisplay()) {
-                        if (!w.mLastHidden) {
-                            //dump();
-                            w.mLastHidden = true;
-                            if (SHOW_TRANSACTIONS) logSurface(w,
-                                    "HIDE (performLayout)", null);
-                            if (w.mSurface != null) {
-                                w.mSurfaceShown = false;
-                                try {
-                                    w.mSurface.hide();
-                                } catch (RuntimeException e) {
-                                    Slog.w(TAG, "Exception hiding surface in " + w);
-                                }
-                            }
-                        }
-                        // If we are waiting for this window to handle an
-                        // orientation change, well, it is hidden, so
-                        // doesn't really matter.  Note that this does
-                        // introduce a potential glitch if the window
-                        // becomes unhidden before it has drawn for the
-                        // new orientation.
-                        if (w.mOrientationChanging) {
-                            w.mOrientationChanging = false;
-                            if (DEBUG_ORIENTATION) Slog.v(TAG,
-                                    "Orientation change skips hidden " + w);
-                        }
-                    } else if (w.mLastLayer != w.mAnimLayer
-                            || w.mLastAlpha != w.mShownAlpha
-                            || w.mLastDsDx != w.mDsDx
-                            || w.mLastDtDx != w.mDtDx
-                            || w.mLastDsDy != w.mDsDy
-                            || w.mLastDtDy != w.mDtDy
-                            || w.mLastHScale != w.mHScale
-                            || w.mLastVScale != w.mVScale
-                            || w.mLastHidden) {
-                        displayed = true;
-                        w.mLastAlpha = w.mShownAlpha;
-                        w.mLastLayer = w.mAnimLayer;
-                        w.mLastDsDx = w.mDsDx;
-                        w.mLastDtDx = w.mDtDx;
-                        w.mLastDsDy = w.mDsDy;
-                        w.mLastDtDy = w.mDtDy;
-                        w.mLastHScale = w.mHScale;
-                        w.mLastVScale = w.mVScale;
-                        if (SHOW_TRANSACTIONS) logSurface(w,
-                                "alpha=" + w.mShownAlpha + " layer=" + w.mAnimLayer
-                                + " matrix=[" + (w.mDsDx*w.mHScale)
-                                + "," + (w.mDtDx*w.mVScale)
-                                + "][" + (w.mDsDy*w.mHScale)
-                                + "," + (w.mDtDy*w.mVScale) + "]", null);
-                        if (w.mSurface != null) {
-                            try {
-                                w.mSurfaceAlpha = w.mShownAlpha;
-                                w.mSurface.setAlpha(w.mShownAlpha);
-                                w.mSurfaceLayer = w.mAnimLayer;
-                                w.mSurface.setLayer(w.mAnimLayer);
-                                w.mSurface.setMatrix(
-                                        w.mDsDx*w.mHScale, w.mDtDx*w.mVScale,
-                                        w.mDsDy*w.mHScale, w.mDtDy*w.mVScale);
-                            } catch (RuntimeException e) {
-                                Slog.w(TAG, "Error updating surface in " + w, e);
-                                if (!recoveringMemory) {
-                                    reclaimSomeSurfaceMemoryLocked(w, "update", true);
-                                }
-                            }
-                        }
-
-                        if (w.mLastHidden && !w.mDrawPending
-                                && !w.mCommitDrawPending
-                                && !w.mReadyToShow) {
-                            if (SHOW_TRANSACTIONS) logSurface(w,
-                                    "SHOW (performLayout)", null);
-                            if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + w
-                                    + " during relayout");
-                            if (showSurfaceRobustlyLocked(w)) {
-                                w.mHasDrawn = true;
-                                w.mLastHidden = false;
-                            } else {
-                                w.mOrientationChanging = false;
-                            }
-                        }
-                        if (w.mSurface != null) {
-                            w.mToken.hasVisible = true;
-                        }
-                    } else {
-                        displayed = true;
-                    }
-
-                    if (displayed) {
-                        if (!covered) {
-                            if (attrs.width == LayoutParams.MATCH_PARENT
-                                    && attrs.height == LayoutParams.MATCH_PARENT) {
-                                covered = true;
-                            }
-                        }
-                        if (w.mOrientationChanging) {
-                            if (w.mDrawPending || w.mCommitDrawPending) {
-                                orientationChangeComplete = false;
-                                if (DEBUG_ORIENTATION) Slog.v(TAG,
-                                        "Orientation continue waiting for draw in " + w);
-                            } else {
-                                w.mOrientationChanging = false;
-                                if (DEBUG_ORIENTATION) Slog.v(TAG,
-                                        "Orientation change complete in " + w);
-                            }
-                        }
-                        w.mToken.hasVisible = true;
-                    }
+                    prepareSurfaceLocked(w, recoveringMemory);
                 } else if (w.mOrientationChanging) {
-                    if (DEBUG_ORIENTATION) Slog.v(TAG,
-                            "Orientation change skips hidden " + w);
+                    if (DEBUG_ORIENTATION) {
+                        Slog.v(TAG, "Orientation change skips hidden " + w);
+                    }
                     w.mOrientationChanging = false;
                 }
 
@@ -8588,92 +8799,12 @@
                     focusDisplayed = true;
                 }
 
-                final boolean obscuredChanged = w.mObscured != obscured;
+                final boolean obscuredChanged = w.mObscured != mInnerFields.mObscured;
 
                 // Update effect.
-                if (!(w.mObscured=obscured)) {
-                    if (w.mSurface != null) {
-                        if ((attrFlags&FLAG_KEEP_SCREEN_ON) != 0) {
-                            holdScreen = w.mSession;
-                        }
-                        if (!syswin && w.mAttrs.screenBrightness >= 0
-                                && screenBrightness < 0) {
-                            screenBrightness = w.mAttrs.screenBrightness;
-                        }
-                        if (!syswin && w.mAttrs.buttonBrightness >= 0
-                                && buttonBrightness < 0) {
-                            buttonBrightness = w.mAttrs.buttonBrightness;
-                        }
-                        if (canBeSeen
-                                && (attrs.type == WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG
-                                 || attrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD
-                                 || attrs.type == WindowManager.LayoutParams.TYPE_SYSTEM_ERROR)) {
-                            syswin = true;
-                        }
-                    }
-
-                    boolean opaqueDrawn = canBeSeen && w.isOpaqueDrawn();
-                    if (opaqueDrawn && w.isFullscreen(innerDw, innerDh)) {
-                        // This window completely covers everything behind it,
-                        // so we want to leave all of them as unblurred (for
-                        // performance reasons).
-                        obscured = true;
-                    } else if (canBeSeen && !obscured &&
-                            (attrFlags&FLAG_BLUR_BEHIND|FLAG_DIM_BEHIND) != 0) {
-                        if (localLOGV) Slog.v(TAG, "Win " + w
-                                + ": blurring=" + blurring
-                                + " obscured=" + obscured
-                                + " displayed=" + displayed);
-                        if ((attrFlags&FLAG_DIM_BEHIND) != 0) {
-                            if (!dimming) {
-                                //Slog.i(TAG, "DIM BEHIND: " + w);
-                                dimming = true;
-                                if (mDimAnimator == null) {
-                                    mDimAnimator = new DimAnimator(mFxSession);
-                                }
-                                mDimAnimator.show(innerDw, innerDh);
-                                mDimAnimator.updateParameters(mContext.getResources(),
-                                        w, currentTime);
-                            }
-                        }
-                        if ((attrFlags&FLAG_BLUR_BEHIND) != 0) {
-                            if (!blurring) {
-                                //Slog.i(TAG, "BLUR BEHIND: " + w);
-                                blurring = true;
-                                if (mBlurSurface == null) {
-                                    try {
-                                        mBlurSurface = new Surface(mFxSession, 0,
-                                                "BlurSurface",
-                                                -1, 16, 16,
-                                                PixelFormat.OPAQUE,
-                                                Surface.FX_SURFACE_BLUR);
-                                    } catch (Exception e) {
-                                        Slog.e(TAG, "Exception creating Blur surface", e);
-                                    }
-                                    if (SHOW_TRANSACTIONS) Slog.i(TAG, "  BLUR "
-                                            + mBlurSurface + ": CREATE");
-                                }
-                                if (mBlurSurface != null) {
-                                    if (SHOW_TRANSACTIONS) Slog.i(TAG, "  BLUR "
-                                            + mBlurSurface + ": pos=(0,0) (" +
-                                            dw + "x" + dh + "), layer=" + (w.mAnimLayer-1));
-                                    mBlurSurface.setPosition(0, 0);
-                                    mBlurSurface.setSize(dw, dh);
-                                    mBlurSurface.setLayer(w.mAnimLayer-LAYER_OFFSET_BLUR);
-                                    if (!mBlurShown) {
-                                        try {
-                                            if (SHOW_TRANSACTIONS) Slog.i(TAG, "  BLUR "
-                                                    + mBlurSurface + ": SHOW");
-                                            mBlurSurface.show();
-                                        } catch (RuntimeException e) {
-                                            Slog.w(TAG, "Failure showing blur surface", e);
-                                        }
-                                        mBlurShown = true;
-                                    }
-                                }
-                            }
-                        }
-                    }
+                w.mObscured = mInnerFields.mObscured;
+                if (!mInnerFields.mObscured) {
+                    handleNotObscuredLocked(w, currentTime, innerDw, innerDh);
                 }
 
                 if (obscuredChanged && mWallpaperTarget == w) {
@@ -8685,11 +8816,12 @@
             }
 
             if (mDimAnimator != null && mDimAnimator.mDimShown) {
-                animating |= mDimAnimator.updateSurface(dimming, currentTime,
-                        mDisplayFrozen || !mDisplayEnabled || !mPolicy.isScreenOnFully());
+                mInnerFields.mAnimating |=
+                        mDimAnimator.updateSurface(mInnerFields.mDimming, currentTime,
+                            mDisplayFrozen || !mDisplayEnabled || !mPolicy.isScreenOnFully());
             }
 
-            if (!blurring && mBlurShown) {
+            if (!mInnerFields.mBlurring && mBlurShown) {
                 if (SHOW_TRANSACTIONS) Slog.i(TAG, "  BLUR " + mBlurSurface
                         + ": HIDE");
                 try {
@@ -8723,8 +8855,8 @@
 
         if (DEBUG_ORIENTATION && mDisplayFrozen) Slog.v(TAG,
                 "With display frozen, orientationChangeComplete="
-                + orientationChangeComplete);
-        if (orientationChangeComplete) {
+                + mInnerFields.mOrientationChangeComplete);
+        if (mInnerFields.mOrientationChangeComplete) {
             if (mWindowsFreezingScreen) {
                 mWindowsFreezingScreen = false;
                 mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
@@ -8816,7 +8948,7 @@
 
         boolean needRelayout = false;
 
-        if (!animating && mAppTransitionRunning) {
+        if (!mInnerFields.mAnimating && mAppTransitionRunning) {
             // We have finished the animation of an app transition.  To do
             // this, we have delayed a lot of operations like showing and
             // hiding apps, moving apps in Z-order, etc.  The app token list
@@ -8839,31 +8971,31 @@
         }
         if (needRelayout) {
             requestTraversalLocked();
-        } else if (animating) {
+        } else if (mInnerFields.mAnimating) {
             mChoreographer.scheduleAnimation();
         }
 
         // Finally update all input windows now that the window changes have stabilized.
         mInputMonitor.updateInputWindowsLw(true /*force*/);
 
-        setHoldScreenLocked(holdScreen != null);
+        setHoldScreenLocked(mInnerFields.mHoldScreen != null);
         if (!mDisplayFrozen) {
-            if (screenBrightness < 0 || screenBrightness > 1.0f) {
+            if (mInnerFields.mScreenBrightness < 0 || mInnerFields.mScreenBrightness > 1.0f) {
                 mPowerManager.setScreenBrightnessOverride(-1);
             } else {
                 mPowerManager.setScreenBrightnessOverride((int)
-                        (screenBrightness * Power.BRIGHTNESS_ON));
+                        (mInnerFields.mScreenBrightness * Power.BRIGHTNESS_ON));
             }
-            if (buttonBrightness < 0 || buttonBrightness > 1.0f) {
+            if (mInnerFields.mButtonBrightness < 0 || mInnerFields.mButtonBrightness > 1.0f) {
                 mPowerManager.setButtonBrightnessOverride(-1);
             } else {
                 mPowerManager.setButtonBrightnessOverride((int)
-                        (buttonBrightness * Power.BRIGHTNESS_ON));
+                        (mInnerFields.mButtonBrightness * Power.BRIGHTNESS_ON));
             }
         }
-        if (holdScreen != mHoldingScreenOn) {
-            mHoldingScreenOn = holdScreen;
-            Message m = mH.obtainMessage(H.HOLD_SCREEN_CHANGED, holdScreen);
+        if (mInnerFields.mHoldScreen != mHoldingScreenOn) {
+            mHoldingScreenOn = mInnerFields.mHoldScreen;
+            Message m = mH.obtainMessage(H.HOLD_SCREEN_CHANGED, mInnerFields.mHoldScreen);
             mH.sendMessage(m);
         }
 
@@ -8873,23 +9005,22 @@
                     LocalPowerManager.BUTTON_EVENT, true);
             mTurnOnScreen = false;
         }
-        
-        if (screenRotationFinished && mScreenRotationAnimation != null) {
+
+        if (updateRotation && mScreenRotationAnimation != null) {
             mScreenRotationAnimation.kill();
             mScreenRotationAnimation = null;
         }
 
         if (updateRotation) {
             if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation");
-            boolean changed = updateRotationUncheckedLocked(false);
-            if (changed) {
+            if (updateRotationUncheckedLocked(false)) {
                 mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
             } else {
                 updateRotation = false;
             }
         }
 
-        if (orientationChangeComplete && !needRelayout && !updateRotation) {
+        if (mInnerFields.mOrientationChangeComplete && !needRelayout && !updateRotation) {
             checkDrawnWindowsLocked();
         }
 
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index fc211e5..42e280f 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -32,7 +32,7 @@
 endif
 ifeq ($(TARGET_BOARD_PLATFORM), omap4)
 	LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY
-	LOCAL_CFLAGS += -DUSE_TRIPLE_BUFFERING=1
+	LOCAL_CFLAGS += -DUSE_TRIPLE_BUFFERING
 endif
 ifeq ($(TARGET_BOARD_PLATFORM), s5pc110)
 	LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY -DNEVER_DEFAULT_TO_ASYNC_MODE
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
index f4afeea..69f1aca 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 The Android Open Source Project
+ * 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.
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <assert.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -22,15 +21,6 @@
 
 #include <unistd.h>
 #include <fcntl.h>
-#include <signal.h>
-#include <termios.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/resource.h>
-
-#include <linux/unistd.h>
 
 #include <utils/Log.h>
 
@@ -45,39 +35,22 @@
 
 // ----------------------------------------------------------------------------
 
-DisplayHardwareBase::DisplayEventThreadBase::DisplayEventThreadBase(
+DisplayHardwareBase::DisplayEventThread::DisplayEventThread(
         const sp<SurfaceFlinger>& flinger)
     : Thread(false), mFlinger(flinger) {
 }
 
-DisplayHardwareBase::DisplayEventThreadBase::~DisplayEventThreadBase() {
+DisplayHardwareBase::DisplayEventThread::~DisplayEventThread() {
 }
 
-// ----------------------------------------------------------------------------
-
-DisplayHardwareBase::DisplayEventThread::DisplayEventThread(
-        const sp<SurfaceFlinger>& flinger)
-    : DisplayEventThreadBase(flinger)
-{
+status_t DisplayHardwareBase::DisplayEventThread::initCheck() const {
+    return ((access(kSleepFileName, R_OK) == 0 &&
+            access(kWakeFileName, R_OK) == 0)) ? NO_ERROR : NO_INIT;
 }
 
-DisplayHardwareBase::DisplayEventThread::~DisplayEventThread()
-{
-}
+bool DisplayHardwareBase::DisplayEventThread::threadLoop() {
 
-bool DisplayHardwareBase::DisplayEventThread::threadLoop()
-{
-    int err = 0;
-    char buf;
-    int fd;
-
-    fd = open(kSleepFileName, O_RDONLY, 0);
-    do {
-      err = read(fd, &buf, 1);
-    } while (err < 0 && errno == EINTR);
-    close(fd);
-    ALOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno));
-    if (err >= 0) {
+    if (waitForFbSleep() == NO_ERROR) {
         sp<SurfaceFlinger> flinger = mFlinger.promote();
         ALOGD("About to give-up screen, flinger = %p", flinger.get());
         if (flinger != 0) {
@@ -85,39 +58,51 @@
             flinger->screenReleased(0);
             mBarrier.wait();
         }
+        if (waitForFbWake() == NO_ERROR) {
+            sp<SurfaceFlinger> flinger = mFlinger.promote();
+            ALOGD("Screen about to return, flinger = %p", flinger.get());
+            if (flinger != 0) {
+                flinger->screenAcquired(0);
+            }
+            return true;
+        }
     }
-    fd = open(kWakeFileName, O_RDONLY, 0);
+
+    // error, exit the thread
+    return false;
+}
+
+status_t DisplayHardwareBase::DisplayEventThread::waitForFbSleep() {
+    int err = 0;
+    char buf;
+    int fd = open(kSleepFileName, O_RDONLY, 0);
+    // if the file doesn't exist, the error will be caught in read() below
     do {
-      err = read(fd, &buf, 1);
+        err = read(fd, &buf, 1);
     } while (err < 0 && errno == EINTR);
     close(fd);
-    ALOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno));
-    if (err >= 0) {
-        sp<SurfaceFlinger> flinger = mFlinger.promote();
-        ALOGD("Screen about to return, flinger = %p", flinger.get());
-        if (flinger != 0)
-            flinger->screenAcquired(0);
-    }
-    return true;
+    ALOGE_IF(err<0, "*** ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno));
+    return err < 0 ? -errno : int(NO_ERROR);
 }
 
-status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const
-{
+status_t DisplayHardwareBase::DisplayEventThread::waitForFbWake() {
+    int err = 0;
+    char buf;
+    int fd = open(kWakeFileName, O_RDONLY, 0);
+    // if the file doesn't exist, the error will be caught in read() below
+    do {
+        err = read(fd, &buf, 1);
+    } while (err < 0 && errno == EINTR);
+    close(fd);
+    ALOGE_IF(err<0, "*** ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno));
+    return err < 0 ? -errno : int(NO_ERROR);
+}
+
+status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const {
     mBarrier.open();
     return NO_ERROR;
 }
 
-status_t DisplayHardwareBase::DisplayEventThread::readyToRun()
-{
-    return NO_ERROR;
-}
-
-status_t DisplayHardwareBase::DisplayEventThread::initCheck() const
-{
-    return ((access(kSleepFileName, R_OK) == 0 &&
-            access(kWakeFileName, R_OK) == 0)) ? NO_ERROR : NO_INIT;
-}
-
 // ----------------------------------------------------------------------------
 
 DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger,
@@ -127,35 +112,35 @@
     mDisplayEventThread = new DisplayEventThread(flinger);
 }
 
-DisplayHardwareBase::~DisplayHardwareBase()
-{
+void DisplayHardwareBase::startSleepManagement() const {
+    if (mDisplayEventThread->initCheck() == NO_ERROR) {
+        mDisplayEventThread->run("DisplayEventThread", PRIORITY_URGENT_DISPLAY);
+    } else {
+        ALOGW("/sys/power/wait_for_fb_{wake|sleep} don't exist");
+    }
+}
+
+DisplayHardwareBase::~DisplayHardwareBase() {
     // request exit
     mDisplayEventThread->requestExitAndWait();
 }
 
-bool DisplayHardwareBase::canDraw() const
-{
+bool DisplayHardwareBase::canDraw() const {
     return mScreenAcquired;
 }
 
-void DisplayHardwareBase::releaseScreen() const
-{
+void DisplayHardwareBase::releaseScreen() const {
     status_t err = mDisplayEventThread->releaseScreen();
     if (err >= 0) {
         mScreenAcquired = false;
     }
 }
 
-void DisplayHardwareBase::acquireScreen() const
-{
-    status_t err = mDisplayEventThread->acquireScreen();
-    if (err >= 0) {
-        mScreenAcquired = true;
-    }
+void DisplayHardwareBase::acquireScreen() const {
+    mScreenAcquired = true;
 }
 
-bool DisplayHardwareBase::isScreenAcquired() const
-{
+bool DisplayHardwareBase::isScreenAcquired() const {
     return mScreenAcquired;
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
index ef2df43..fba211b 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 The Android Open Source Project
+ * 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.
@@ -20,8 +20,6 @@
 #include <stdint.h>
 #include <utils/RefBase.h>
 #include <utils/threads.h>
-#include <linux/kd.h>
-#include <linux/vt.h>
 #include "Barrier.h"
 
 namespace android {
@@ -31,11 +29,13 @@
 class DisplayHardwareBase
 {
 public:
-                DisplayHardwareBase(
-                        const sp<SurfaceFlinger>& flinger,
-                        uint32_t displayIndex);
+    DisplayHardwareBase(
+            const sp<SurfaceFlinger>& flinger,
+            uint32_t displayIndex);
 
-                ~DisplayHardwareBase();
+    ~DisplayHardwareBase();
+
+    void startSleepManagement() const;
 
     // console management
     void releaseScreen() const;
@@ -46,34 +46,21 @@
 
 
 private:
-    class DisplayEventThreadBase : public Thread {
-    protected:
+    class DisplayEventThread : public Thread {
         wp<SurfaceFlinger> mFlinger;
-    public:
-        DisplayEventThreadBase(const sp<SurfaceFlinger>& flinger);
-        virtual ~DisplayEventThreadBase();
-        virtual void onFirstRef() {
-            run("DisplayEventThread", PRIORITY_URGENT_DISPLAY);
-        }
-        virtual status_t acquireScreen() const { return NO_ERROR; };
-        virtual status_t releaseScreen() const { return NO_ERROR; };
-        virtual status_t initCheck() const = 0;
-    };
-
-    class DisplayEventThread : public DisplayEventThreadBase 
-    {
         mutable Barrier mBarrier;
+        status_t waitForFbSleep();
+        status_t waitForFbWake();
     public:
-                DisplayEventThread(const sp<SurfaceFlinger>& flinger);
+        DisplayEventThread(const sp<SurfaceFlinger>& flinger);
         virtual ~DisplayEventThread();
         virtual bool threadLoop();
-        virtual status_t readyToRun();
-        virtual status_t releaseScreen() const;
-        virtual status_t initCheck() const;
+        status_t releaseScreen() const;
+        status_t initCheck() const;
     };
 
-    sp<DisplayEventThreadBase>  mDisplayEventThread;
-    mutable int                 mScreenAcquired;
+    sp<DisplayEventThread>  mDisplayEventThread;
+    mutable int             mScreenAcquired;
 };
 
 }; // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 9c04d59..3e6b872 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -55,6 +55,7 @@
         mCurrentTransform(0),
         mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
         mCurrentOpacity(true),
+        mRefreshPending(0),
         mFrameLatencyNeeded(false),
         mFrameLatencyOffset(0),
         mFormat(PIXEL_FORMAT_NONE),
@@ -113,7 +114,7 @@
 
 void Layer::onFrameQueued() {
     android_atomic_inc(&mQueuedFrames);
-    mFlinger->signalEvent();
+    mFlinger->signalLayerUpdate();
 }
 
 // called with SurfaceFlinger::mStateLock as soon as the layer is entered
@@ -407,16 +408,37 @@
 // pageflip handling...
 // ----------------------------------------------------------------------------
 
+bool Layer::onPreComposition()
+{
+    // if there was more than one pending update, request a refresh
+    if (mRefreshPending >= 2) {
+        mRefreshPending = 0;
+        return true;
+    }
+    mRefreshPending = 0;
+    return false;
+}
+
 void Layer::lockPageFlip(bool& recomputeVisibleRegions)
 {
     if (mQueuedFrames > 0) {
+
+        // if we've already called updateTexImage() without going through
+        // a composition step, we have to skip this layer at this point
+        // because we cannot call updateTeximage() without a corresponding
+        // compositionComplete() call.
+        // we'll trigger an update in onPreComposition().
+        if (mRefreshPending++) {
+            return;
+        }
+
         // Capture the old state of the layer for comparisons later
         const bool oldOpacity = isOpaque();
         sp<GraphicBuffer> oldActiveBuffer = mActiveBuffer;
 
         // signal another event if we have more frames pending
         if (android_atomic_dec(&mQueuedFrames) > 1) {
-            mFlinger->signalEvent();
+            mFlinger->signalLayerUpdate();
         }
 
         if (mSurfaceTexture->updateTexImage() < NO_ERROR) {
@@ -519,6 +541,10 @@
 void Layer::unlockPageFlip(
         const Transform& planeTransform, Region& outDirtyRegion)
 {
+    if (mRefreshPending >= 2) {
+        return;
+    }
+
     Region dirtyRegion(mPostedDirtyRegion);
     if (!dirtyRegion.isEmpty()) {
         mPostedDirtyRegion.clear();
@@ -552,9 +578,9 @@
     snprintf(buffer, SIZE,
             "      "
             "format=%2d, activeBuffer=[%4ux%4u:%4u,%3X],"
-            " transform-hint=0x%02x, queued-frames=%d\n",
+            " transform-hint=0x%02x, queued-frames=%d, mRefreshPending=%d\n",
             mFormat, w0, h0, s0,f0,
-            getTransformHint(), mQueuedFrames);
+            getTransformHint(), mQueuedFrames, mRefreshPending);
 
     result.append(buffer);
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 2dd4da1..bf30608 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -80,6 +80,7 @@
     virtual wp<IBinder> getSurfaceTextureBinder() const;
 
     virtual void onLayerDisplayed();
+    virtual bool onPreComposition();
 
     // only for debugging
     inline const sp<GraphicBuffer>& getActiveBuffer() const { return mActiveBuffer; }
@@ -115,14 +116,17 @@
     uint32_t mCurrentTransform;
     uint32_t mCurrentScalingMode;
     bool mCurrentOpacity;
+    size_t mRefreshPending;
     bool mFrameLatencyNeeded;
     int mFrameLatencyOffset;
+
     struct Statistics {
         Statistics() : timestamp(0), set(0), vsync(0) { }
         nsecs_t timestamp;  // buffer timestamp
         nsecs_t set;        // buffer displayed timestamp
         nsecs_t vsync;      // vsync immediately before set
     };
+
     // protected by mLock
     Statistics mFrameStats[128];
 
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index d32fcdd..e764001 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -47,8 +47,7 @@
       mOrientation(0),
       mPlaneOrientation(0),
       mTransactionFlags(0),
-      mPremultipliedAlpha(true), mName("unnamed"), mDebug(false),
-      mInvalidate(0)
+      mPremultipliedAlpha(true), mName("unnamed"), mDebug(false)
 {
     const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
     mFlags = hw.getFlags();
@@ -262,23 +261,11 @@
     mTransformedBounds = tr.makeBounds(w, h);
 }
 
-void LayerBase::lockPageFlip(bool& recomputeVisibleRegions)
-{
+void LayerBase::lockPageFlip(bool& recomputeVisibleRegions) {
 }
 
 void LayerBase::unlockPageFlip(
-        const Transform& planeTransform, Region& outDirtyRegion)
-{
-    if ((android_atomic_and(~1, &mInvalidate)&1) == 1) {
-        outDirtyRegion.orSelf(visibleRegionScreen);
-    }
-}
-
-void LayerBase::invalidate()
-{
-    if ((android_atomic_or(1, &mInvalidate)&1) == 0) {
-        mFlinger->signalEvent();
-    }
+        const Transform& planeTransform, Region& outDirtyRegion) {
 }
 
 void LayerBase::drawRegion(const Region& reg) const
@@ -471,16 +458,21 @@
 void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const
 {
     const Layer::State& s(drawingState());
+
+    snprintf(buffer, SIZE,
+            "+ %s %p (%s)\n",
+            getTypeId(), this, getName().string());
+    result.append(buffer);
+
     s.transparentRegion.dump(result, "transparentRegion");
     transparentRegionScreen.dump(result, "transparentRegionScreen");
     visibleRegionScreen.dump(result, "visibleRegionScreen");
+
     snprintf(buffer, SIZE,
-            "+ %s %p (%s)\n"
             "      "
             "z=%9d, pos=(%g,%g), size=(%4d,%4d), "
             "isOpaque=%1d, needsDithering=%1d, invalidate=%1d, "
             "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n",
-            getTypeId(), this, getName().string(),
             s.z, s.transform.tx(), s.transform.ty(), s.w, s.h,
             isOpaque(), needsDithering(), contentDirty,
             s.alpha, s.flags,
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index 6b62c9d..b8f7680 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -103,8 +103,6 @@
             Rect visibleBounds() const;
             void drawRegion(const Region& reg) const;
 
-            void invalidate();
-
     virtual sp<LayerBaseClient> getLayerBaseClient() const { return 0; }
     virtual sp<Layer> getLayer() const { return 0; }
 
@@ -204,9 +202,16 @@
 
     /** called with the state lock when the surface is removed from the
      *  current list */
-    virtual void onRemoved() { };
+    virtual void onRemoved() { }
 
-    virtual void onLayerDisplayed() { };
+    /** called after page-flip
+     */
+    virtual void onLayerDisplayed() { }
+
+    /** called before composition.
+     * returns true if the layer has pending updates.
+     */
+    virtual bool onPreComposition() { return false; }
 
     /** always call base class first */
     virtual void dump(String8& result, char* scratch, size_t size) const;
@@ -275,10 +280,6 @@
     mutable     bool            mDebug;
 
 
-                // atomic
-    volatile    int32_t         mInvalidate;
-                
-
 public:
     // called from class SurfaceFlinger
     virtual ~LayerBase();
diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp
index 70711e7..290fff4 100644
--- a/services/surfaceflinger/MessageQueue.cpp
+++ b/services/surfaceflinger/MessageQueue.cpp
@@ -29,6 +29,7 @@
 
 #include "MessageQueue.h"
 #include "EventThread.h"
+#include "SurfaceFlinger.h"
 
 namespace android {
 
@@ -48,14 +49,47 @@
 
 // ---------------------------------------------------------------------------
 
+void MessageQueue::Handler::signalRefresh() {
+    if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) {
+        mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH));
+    }
+}
+
+void MessageQueue::Handler::signalInvalidate() {
+    if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) {
+        mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
+    }
+}
+
+void MessageQueue::Handler::handleMessage(const Message& message) {
+    switch (message.what) {
+        case INVALIDATE:
+            android_atomic_and(~eventMaskInvalidate, &mEventMask);
+            mQueue.mFlinger->onMessageReceived(message.what);
+            break;
+        case REFRESH:
+            android_atomic_and(~eventMaskRefresh, &mEventMask);
+            mQueue.mFlinger->onMessageReceived(message.what);
+            break;
+    }
+}
+
+// ---------------------------------------------------------------------------
+
 MessageQueue::MessageQueue()
-    : mLooper(new Looper(true)), mWorkPending(0)
 {
 }
 
 MessageQueue::~MessageQueue() {
 }
 
+void MessageQueue::init(const sp<SurfaceFlinger>& flinger)
+{
+    mFlinger = flinger;
+    mLooper = new Looper(true);
+    mHandler = new Handler(*this);
+}
+
 void MessageQueue::setEventThread(const sp<EventThread>& eventThread)
 {
     mEventThread = eventThread;
@@ -68,25 +102,16 @@
 void MessageQueue::waitMessage() {
     do {
         IPCThreadState::self()->flushCommands();
-
         int32_t ret = mLooper->pollOnce(-1);
         switch (ret) {
             case ALOOPER_POLL_WAKE:
             case ALOOPER_POLL_CALLBACK:
-                // callback and/or wake
-                if (android_atomic_and(0, &mWorkPending)) {
-                    return;
-                }
                 continue;
-
+            case ALOOPER_POLL_ERROR:
+                ALOGE("ALOOPER_POLL_ERROR");
             case ALOOPER_POLL_TIMEOUT:
                 // timeout (should not happen)
                 continue;
-
-            case ALOOPER_POLL_ERROR:
-                ALOGE("ALOOPER_POLL_ERROR");
-                continue;
-
             default:
                 // should not happen
                 ALOGE("Looper::pollOnce() returned unknown status %d", ret);
@@ -107,15 +132,13 @@
     return NO_ERROR;
 }
 
-void MessageQueue::scheduleWorkASAP() {
-    if (android_atomic_or(1, &mWorkPending) == 0) {
-        mLooper->wake();
-   }
+void MessageQueue::invalidate() {
+//    mHandler->signalInvalidate();
+    mEvents->requestNextVsync();
 }
 
-status_t MessageQueue::invalidate() {
+void MessageQueue::refresh() {
     mEvents->requestNextVsync();
-    return NO_ERROR;
 }
 
 int MessageQueue::cb_eventReceiver(int fd, int events, void* data) {
@@ -126,10 +149,10 @@
 int MessageQueue::eventReceiver(int fd, int events) {
     ssize_t n;
     DisplayEventReceiver::Event buffer[8];
-    while ((n = getEvents(buffer, 8)) > 0) {
+    while ((n = DisplayEventReceiver::getEvents(mEventTube, buffer, 8)) > 0) {
         for (int i=0 ; i<n ; i++) {
             if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
-                scheduleWorkASAP();
+                mHandler->signalRefresh();
                 break;
             }
         }
@@ -137,24 +160,6 @@
     return 1;
 }
 
-ssize_t MessageQueue::getEvents(
-        DisplayEventReceiver::Event* events, size_t count)
-{
-    ssize_t size = mEventTube->read(events, sizeof(events[0])*count);
-    ALOGE_IF(size<0, "MessageQueue::getEvents error (%s)", strerror(-size));
-    if (size >= 0) {
-        // Note: if (size % sizeof(events[0])) != 0, we've got a
-        // partial read. This can happen if the queue filed up (ie: if we
-        // didn't pull from it fast enough).
-        // We discard the partial event and rely on the sender to
-        // re-send the event if appropriate (some events, like VSYNC
-        // can be lost forever).
-        // returns number of events read
-        size /= sizeof(events[0]);
-    }
-    return size;
-}
-
 // ---------------------------------------------------------------------------
 
 }; // namespace android
diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h
index 5ea197d..ea29e7e 100644
--- a/services/surfaceflinger/MessageQueue.h
+++ b/services/surfaceflinger/MessageQueue.h
@@ -33,6 +33,7 @@
 
 class IDisplayEventConnection;
 class EventThread;
+class SurfaceFlinger;
 
 // ---------------------------------------------------------------------------
 
@@ -59,25 +60,48 @@
 // ---------------------------------------------------------------------------
 
 class MessageQueue {
+    class Handler : public MessageHandler {
+        enum {
+            eventMaskInvalidate = 0x1,
+            eventMaskRefresh    = 0x2
+        };
+        MessageQueue& mQueue;
+        int32_t mEventMask;
+    public:
+        Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) { }
+        virtual void handleMessage(const Message& message);
+        void signalRefresh();
+        void signalInvalidate();
+    };
+
+    friend class Handler;
+
+    sp<SurfaceFlinger> mFlinger;
     sp<Looper> mLooper;
     sp<EventThread> mEventThread;
     sp<IDisplayEventConnection> mEvents;
     sp<BitTube> mEventTube;
-    int32_t mWorkPending;
+    sp<Handler> mHandler;
+
 
     static int cb_eventReceiver(int fd, int events, void* data);
     int eventReceiver(int fd, int events);
-    ssize_t getEvents(DisplayEventReceiver::Event* events, size_t count);
-    void scheduleWorkASAP();
 
 public:
+    enum {
+        INVALIDATE = 0,
+        REFRESH    = 1,
+    };
+
     MessageQueue();
     ~MessageQueue();
+    void init(const sp<SurfaceFlinger>& flinger);
     void setEventThread(const sp<EventThread>& events);
 
     void waitMessage();
     status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime=0);
-    status_t invalidate();
+    void invalidate();
+    void refresh();
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index ff70ec3..ab09bfa 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -125,11 +125,34 @@
     ALOGI_IF(mDebugDDMS,         "DDMS debugging enabled");
 }
 
+void SurfaceFlinger::onFirstRef()
+{
+    mEventQueue.init(this);
+
+    run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY);
+
+    // Wait for the main thread to be done with its initialization
+    mReadyToRunBarrier.wait();
+}
+
+
 SurfaceFlinger::~SurfaceFlinger()
 {
     glDeleteTextures(1, &mWormholeTexName);
 }
 
+void SurfaceFlinger::binderDied(const wp<IBinder>& who)
+{
+    // the window manager died on us. prepare its eulogy.
+
+    // reset screen orientation
+    Vector<ComposerState> state;
+    setTransactionState(state, eOrientationDefault, 0);
+
+    // restart the boot-animation
+    property_set("ctl.start", "bootanim");
+}
+
 sp<IMemoryHeap> SurfaceFlinger::getCblk() const
 {
     return mServerHeap;
@@ -183,26 +206,6 @@
     property_set("ctl.stop", "bootanim");
 }
 
-void SurfaceFlinger::binderDied(const wp<IBinder>& who)
-{
-    // the window manager died on us. prepare its eulogy.
-
-    // reset screen orientation
-    Vector<ComposerState> state;
-    setTransactionState(state, eOrientationDefault, 0);
-
-    // restart the boot-animation
-    property_set("ctl.start", "bootanim");
-}
-
-void SurfaceFlinger::onFirstRef()
-{
-    run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY);
-
-    // Wait for the main thread to be done with its initialization
-    mReadyToRunBarrier.wait();
-}
-
 static inline uint16_t pack565(int r, int g, int b) {
     return (r<<11)|(g<<5)|b;
 }
@@ -297,6 +300,7 @@
     // start the EventThread
     mEventThread = new EventThread(this);
     mEventQueue.setEventThread(mEventThread);
+    hw.startSleepManagement();
 
     /*
      *  We're now ready to accept clients...
@@ -311,34 +315,6 @@
 }
 
 // ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark Events Handler
-#endif
-
-void SurfaceFlinger::waitForEvent() {
-    mEventQueue.waitMessage();
-}
-
-void SurfaceFlinger::signalEvent() {
-    mEventQueue.invalidate();
-}
-
-status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg,
-        nsecs_t reltime, uint32_t flags) {
-    return mEventQueue.postMessage(msg, reltime);
-}
-
-status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg,
-        nsecs_t reltime, uint32_t flags) {
-    status_t res = mEventQueue.postMessage(msg, reltime);
-    if (res == NO_ERROR) {
-        msg->wait();
-    }
-    return res;
-}
-
-// ----------------------------------------------------------------------------
 
 bool SurfaceFlinger::authenticateSurfaceTexture(
         const sp<ISurfaceTexture>& surfaceTexture) const {
@@ -388,59 +364,104 @@
 }
 
 // ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark Main loop
-#endif
+
+void SurfaceFlinger::waitForEvent() {
+    mEventQueue.waitMessage();
+}
+
+void SurfaceFlinger::signalTransaction() {
+    mEventQueue.invalidate();
+}
+
+void SurfaceFlinger::signalLayerUpdate() {
+    mEventQueue.invalidate();
+}
+
+void SurfaceFlinger::signalRefresh() {
+    mEventQueue.refresh();
+}
+
+status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg,
+        nsecs_t reltime, uint32_t flags) {
+    return mEventQueue.postMessage(msg, reltime);
+}
+
+status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg,
+        nsecs_t reltime, uint32_t flags) {
+    status_t res = mEventQueue.postMessage(msg, reltime);
+    if (res == NO_ERROR) {
+        msg->wait();
+    }
+    return res;
+}
 
 bool SurfaceFlinger::threadLoop()
 {
     waitForEvent();
-
-    // check for transactions
-    if (CC_UNLIKELY(mConsoleSignals)) {
-        handleConsoleEvents();
-    }
-
-    // if we're in a global transaction, don't do anything.
-    const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
-    uint32_t transactionFlags = peekTransactionFlags(mask);
-    if (CC_UNLIKELY(transactionFlags)) {
-        handleTransaction(transactionFlags);
-    }
-
-    // post surfaces (if needed)
-    handlePageFlip();
-
-    if (mDirtyRegion.isEmpty()) {
-        // nothing new to do.
-        return true;
-    }
-
-    if (CC_UNLIKELY(mHwWorkListDirty)) {
-        // build the h/w work list
-        handleWorkList();
-    }
-
-    const DisplayHardware& hw(graphicPlane(0).displayHardware());
-    if (CC_LIKELY(hw.canDraw())) {
-        // repaint the framebuffer (if needed)
-        handleRepaint();
-        // inform the h/w that we're done compositing
-        hw.compositionComplete();
-        postFramebuffer();
-    } else {
-        // pretend we did the post
-        hw.compositionComplete();
-    }
     return true;
 }
 
+void SurfaceFlinger::onMessageReceived(int32_t what)
+{
+    switch (what) {
+        case MessageQueue::REFRESH: {
+//        case MessageQueue::INVALIDATE: {
+            // check for transactions
+            if (CC_UNLIKELY(mConsoleSignals)) {
+                handleConsoleEvents();
+            }
+
+            // if we're in a global transaction, don't do anything.
+            const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
+            uint32_t transactionFlags = peekTransactionFlags(mask);
+            if (CC_UNLIKELY(transactionFlags)) {
+                handleTransaction(transactionFlags);
+            }
+
+            // post surfaces (if needed)
+            handlePageFlip();
+
+//            signalRefresh();
+//
+//        } break;
+//
+//        case MessageQueue::REFRESH: {
+
+            handleRefresh();
+
+            const DisplayHardware& hw(graphicPlane(0).displayHardware());
+
+//            if (mDirtyRegion.isEmpty()) {
+//                return;
+//            }
+
+            if (CC_UNLIKELY(mHwWorkListDirty)) {
+                // build the h/w work list
+                handleWorkList();
+            }
+
+            if (CC_LIKELY(hw.canDraw())) {
+                // repaint the framebuffer (if needed)
+                handleRepaint();
+                // inform the h/w that we're done compositing
+                hw.compositionComplete();
+                postFramebuffer();
+            } else {
+                // pretend we did the post
+                hw.compositionComplete();
+            }
+
+        } break;
+    }
+}
+
 void SurfaceFlinger::postFramebuffer()
 {
-    // this should never happen. we do the flip anyways so we don't
-    // risk to cause a deadlock with hwc
-    ALOGW_IF(mSwapRegion.isEmpty(), "mSwapRegion is empty");
+    // mSwapRegion can be empty here is some cases, for instance if a hidden
+    // or fully transparent window is updating.
+    // in that case, we need to flip anyways to not risk a deadlock with
+    // h/w composer.
+
     const DisplayHardware& hw(graphicPlane(0).displayHardware());
     const nsecs_t now = systemTime();
     mDebugInSwapBuffers = now;
@@ -717,13 +738,13 @@
 
 void SurfaceFlinger::handlePageFlip()
 {
-    bool visibleRegions = mVisibleRegionsDirty;
-    const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
-    visibleRegions |= lockPageFlip(currentLayers);
+    const DisplayHardware& hw = graphicPlane(0).displayHardware();
+    const Region screenRegion(hw.bounds());
 
-        const DisplayHardware& hw = graphicPlane(0).displayHardware();
-        const Region screenRegion(hw.bounds());
-        if (visibleRegions) {
+    const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
+    const bool visibleRegions = lockPageFlip(currentLayers);
+
+        if (visibleRegions || mVisibleRegionsDirty) {
             Region opaqueRegion;
             computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion);
 
@@ -770,7 +791,7 @@
 {
     const GraphicPlane& plane(graphicPlane(0));
     const Transform& planeTransform(plane.transform());
-    size_t count = currentLayers.size();
+    const size_t count = currentLayers.size();
     sp<LayerBase> const* layers = currentLayers.array();
     for (size_t i=0 ; i<count ; i++) {
         const sp<LayerBase>& layer(layers[i]);
@@ -778,6 +799,23 @@
     }
 }
 
+void SurfaceFlinger::handleRefresh()
+{
+    bool needInvalidate = false;
+    const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
+    const size_t count = currentLayers.size();
+    for (size_t i=0 ; i<count ; i++) {
+        const sp<LayerBase>& layer(currentLayers[i]);
+        if (layer->onPreComposition()) {
+            needInvalidate = true;
+        }
+    }
+    if (needInvalidate) {
+        signalLayerUpdate();
+    }
+}
+
+
 void SurfaceFlinger::handleWorkList()
 {
     mHwWorkListDirty = false;
@@ -1175,7 +1213,7 @@
 {
     uint32_t old = android_atomic_or(flags, &mTransactionFlags);
     if ((old & flags)==0) { // wake the server up
-        signalEvent();
+        signalTransaction();
     }
     return old;
 }
@@ -1426,14 +1464,14 @@
 {
     // this may be called by a signal handler, we can't do too much in here
     android_atomic_or(eConsoleReleased, &mConsoleSignals);
-    signalEvent();
+    signalTransaction();
 }
 
 void SurfaceFlinger::screenAcquired(int dpy)
 {
     // this may be called by a signal handler, we can't do too much in here
     android_atomic_or(eConsoleAcquired, &mConsoleSignals);
-    signalEvent();
+    signalTransaction();
 }
 
 status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
@@ -1620,11 +1658,13 @@
     snprintf(buffer, SIZE,
             "  last eglSwapBuffers() time: %f us\n"
             "  last transaction time     : %f us\n"
+            "  transaction-flags         : %08x\n"
             "  refresh-rate              : %f fps\n"
             "  x-dpi                     : %f\n"
             "  y-dpi                     : %f\n",
             mLastSwapBufferTime/1000.0,
             mLastTransactionTime/1000.0,
+            mTransactionFlags,
             hw.getRefreshRate(),
             hw.getDpiX(),
             hw.getDpiY());
@@ -1769,7 +1809,7 @@
     const DisplayHardware& hw(graphicPlane(0).displayHardware());
     const Rect bounds(hw.getBounds());
     setInvalidateRegion(Region(bounds));
-    signalEvent();
+    signalTransaction();
 }
 
 void SurfaceFlinger::setInvalidateRegion(const Region& reg) {
@@ -2245,7 +2285,7 @@
 
     // make sure to redraw the whole screen when the animation is done
     mDirtyRegion.set(hw.bounds());
-    signalEvent();
+    signalTransaction();
 
     return NO_ERROR;
 }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c24a9de..fcd9361 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -190,6 +190,8 @@
             status_t renderScreenToTextureLocked(DisplayID dpy,
                     GLuint* textureName, GLfloat* uOut, GLfloat* vOut);
 
+            void onMessageReceived(int32_t what);
+
             status_t postMessageAsync(const sp<MessageBase>& msg,
                     nsecs_t reltime=0, uint32_t flags = 0);
 
@@ -283,7 +285,10 @@
 public:     // hack to work around gcc 4.0.3 bug
     const GraphicPlane&     graphicPlane(int dpy) const;
           GraphicPlane&     graphicPlane(int dpy);
-          void              signalEvent();
+
+          void              signalTransaction();
+          void              signalLayerUpdate();
+          void              signalRefresh();
           void              repaintEverything();
 
 private:
@@ -300,6 +305,7 @@
             void        handlePageFlip();
             bool        lockPageFlip(const LayerVector& currentLayers);
             void        unlockPageFlip(const LayerVector& currentLayers);
+            void        handleRefresh();
             void        handleWorkList();
             void        handleRepaint();
             void        postFramebuffer();
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 36a2567..e863f8b 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -21,7 +21,6 @@
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
-import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
@@ -29,6 +28,8 @@
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
 import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
+import static android.net.TrafficStats.KB_IN_BYTES;
+import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT;
@@ -101,10 +102,6 @@
     private static final long TEST_START = 1194220800000L;
     private static final String TEST_IFACE = "test0";
 
-    private static final long KB_IN_BYTES = 1024;
-    private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
-    private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
-
     private static NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi();
 
     private BroadcastInterceptingContext mServiceContext;
@@ -256,41 +253,49 @@
     }
 
     public void testPidForegroundCombined() throws Exception {
+        IdleFuture idle;
+
         // push all uid into background
+        idle = expectIdle();
         mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
         mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
         mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, false);
-        waitUntilIdle();
+        idle.get();
         assertFalse(mService.isUidForeground(UID_A));
         assertFalse(mService.isUidForeground(UID_B));
 
         // push one of the shared pids into foreground
+        idle = expectIdle();
         mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true);
-        waitUntilIdle();
+        idle.get();
         assertTrue(mService.isUidForeground(UID_A));
         assertFalse(mService.isUidForeground(UID_B));
 
         // and swap another uid into foreground
+        idle = expectIdle();
         mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
         mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, true);
-        waitUntilIdle();
+        idle.get();
         assertFalse(mService.isUidForeground(UID_A));
         assertTrue(mService.isUidForeground(UID_B));
 
         // push both pid into foreground
+        idle = expectIdle();
         mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
         mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true);
-        waitUntilIdle();
+        idle.get();
         assertTrue(mService.isUidForeground(UID_A));
 
         // pull one out, should still be foreground
+        idle = expectIdle();
         mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
-        waitUntilIdle();
+        idle.get();
         assertTrue(mService.isUidForeground(UID_A));
 
         // pull final pid out, should now be background
+        idle = expectIdle();
         mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
-        waitUntilIdle();
+        idle.get();
         assertFalse(mService.isUidForeground(UID_A));
     }
 
@@ -434,7 +439,7 @@
         final long expectedCycle = parseTime("2007-11-05T00:00:00.000Z");
 
         final NetworkPolicy policy = new NetworkPolicy(
-                sTemplateWifi, 5, 1024L, 1024L, SNOOZE_NEVER, false);
+                sTemplateWifi, 5, 1024L, 1024L, false);
         final long actualCycle = computeLastCycleBoundary(currentTime, policy);
         assertTimeEquals(expectedCycle, actualCycle);
     }
@@ -445,7 +450,7 @@
         final long expectedCycle = parseTime("2007-10-20T00:00:00.000Z");
 
         final NetworkPolicy policy = new NetworkPolicy(
-                sTemplateWifi, 20, 1024L, 1024L, SNOOZE_NEVER, false);
+                sTemplateWifi, 20, 1024L, 1024L, false);
         final long actualCycle = computeLastCycleBoundary(currentTime, policy);
         assertTimeEquals(expectedCycle, actualCycle);
     }
@@ -456,7 +461,7 @@
         final long expectedCycle = parseTime("2007-01-30T00:00:00.000Z");
 
         final NetworkPolicy policy = new NetworkPolicy(
-                sTemplateWifi, 30, 1024L, 1024L, SNOOZE_NEVER, false);
+                sTemplateWifi, 30, 1024L, 1024L, false);
         final long actualCycle = computeLastCycleBoundary(currentTime, policy);
         assertTimeEquals(expectedCycle, actualCycle);
     }
@@ -467,14 +472,14 @@
         final long expectedCycle = parseTime("2007-02-28T23:59:59.000Z");
 
         final NetworkPolicy policy = new NetworkPolicy(
-                sTemplateWifi, 30, 1024L, 1024L, SNOOZE_NEVER, false);
+                sTemplateWifi, 30, 1024L, 1024L, false);
         final long actualCycle = computeLastCycleBoundary(currentTime, policy);
         assertTimeEquals(expectedCycle, actualCycle);
     }
 
     public void testNextCycleSane() throws Exception {
         final NetworkPolicy policy = new NetworkPolicy(
-                sTemplateWifi, 31, WARNING_DISABLED, LIMIT_DISABLED, SNOOZE_NEVER, false);
+                sTemplateWifi, 31, WARNING_DISABLED, LIMIT_DISABLED, false);
         final LinkedHashSet<Long> seen = new LinkedHashSet<Long>();
 
         // walk forwards, ensuring that cycle boundaries don't get stuck
@@ -489,7 +494,7 @@
 
     public void testLastCycleSane() throws Exception {
         final NetworkPolicy policy = new NetworkPolicy(
-                sTemplateWifi, 31, WARNING_DISABLED, LIMIT_DISABLED, SNOOZE_NEVER, false);
+                sTemplateWifi, 31, WARNING_DISABLED, LIMIT_DISABLED, false);
         final LinkedHashSet<Long> seen = new LinkedHashSet<Long>();
 
         // walk backwards, ensuring that cycle boundaries look sane
@@ -547,7 +552,7 @@
 
         replay();
         setNetworkPolicies(new NetworkPolicy(
-                sTemplateWifi, CYCLE_DAY, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, SNOOZE_NEVER, false));
+                sTemplateWifi, CYCLE_DAY, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, false));
         future.get();
         verifyAndReset();
     }
@@ -604,9 +609,8 @@
             future = expectMeteredIfacesChanged();
 
             replay();
-            setNetworkPolicies(
-                    new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES,
-                            SNOOZE_NEVER, false));
+            setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1 * MB_IN_BYTES,
+                    2 * MB_IN_BYTES, false));
             future.get();
             verifyAndReset();
         }
@@ -698,7 +702,7 @@
             tagFuture = expectEnqueueNotification();
 
             replay();
-            mService.snoozePolicy(sTemplateWifi);
+            mService.snoozeLimit(sTemplateWifi);
             assertNotificationType(TYPE_LIMIT_SNOOZED, tagFuture.get());
             future.get();
             verifyAndReset();
@@ -736,9 +740,8 @@
             future = expectMeteredIfacesChanged(TEST_IFACE);
 
             replay();
-            setNetworkPolicies(
-                    new NetworkPolicy(sTemplateWifi, CYCLE_DAY, WARNING_DISABLED, LIMIT_DISABLED,
-                            SNOOZE_NEVER, true));
+            setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, WARNING_DISABLED,
+                    LIMIT_DISABLED, true));
             future.get();
             verifyAndReset();
         }
@@ -890,10 +893,10 @@
     /**
      * Wait until {@link #mService} internal {@link Handler} is idle.
      */
-    private void waitUntilIdle() throws Exception {
+    private IdleFuture expectIdle() {
         final IdleFuture future = new IdleFuture();
         mService.addIdleHandler(future);
-        future.get();
+        return future;
     }
 
     private static void assertTimeEquals(long expected, long actual) {
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 8f5e77e..daf2018 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -31,6 +31,7 @@
 import static android.net.NetworkStatsHistory.FIELD_ALL;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.NetworkTemplate.buildTemplateWifi;
+import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.net.TrafficStats.UID_REMOVED;
 import static android.net.TrafficStats.UID_TETHERING;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
@@ -92,10 +93,6 @@
     private static final String IMSI_1 = "310004";
     private static final String IMSI_2 = "310260";
 
-    private static final long KB_IN_BYTES = 1024;
-    private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES;
-    private static final long GB_IN_BYTES = 1024 * MB_IN_BYTES;
-
     private static NetworkTemplate sTemplateWifi = buildTemplateWifi();
     private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1);
     private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2);
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 1a4b574..1049669 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -293,10 +293,11 @@
         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)
-                    && (mLteRssnr == INVALID_SNR)
                     && (mLteCqi == -1)) {
                 level = getGsmLevel();
             } else {
@@ -331,7 +332,6 @@
             if ((mLteSignalStrength == -1)
                     && (mLteRsrp == -1)
                     && (mLteRsrq == -1)
-                    && (mLteRssnr == INVALID_SNR)
                     && (mLteCqi == -1)) {
                 asuLevel = getGsmAsuLevel();
             } else {
@@ -367,7 +367,6 @@
             if ((mLteSignalStrength == -1)
                     && (mLteRsrp == -1)
                     && (mLteRsrq == -1)
-                    && (mLteRssnr == INVALID_SNR)
                     && (mLteCqi == -1)) {
                 dBm = getGsmDbm();
             } else {
diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java
index 7900a9d..a0efab2 100644
--- a/telephony/java/com/android/internal/telephony/CommandsInterface.java
+++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java
@@ -759,6 +759,15 @@
      *  retMsg.obj = AsyncResult ar
      *  ar.exception carries exception on failure
      *  ar.userObject contains the orignal value of result.obj
+     *  ar.result is String containing IMSI on success
+     */
+    void getIMSIForApp(String aid, Message result);
+
+    /**
+     *  returned message
+     *  retMsg.obj = AsyncResult ar
+     *  ar.exception carries exception on failure
+     *  ar.userObject contains the orignal value of result.obj
      *  ar.result is String containing IMEI on success
      */
     void getIMEI(Message result);
@@ -1050,6 +1059,14 @@
             String data, String pin2, Message response);
 
     /**
+     * parameters equivalent to 27.007 AT+CRSM command
+     * response.obj will be an AsyncResult
+     * response.obj.userObj will be a IccIoResult on success
+     */
+    void iccIOForApp (int command, int fileid, String path, int p1, int p2, int p3,
+            String data, String pin2, String aid, Message response);
+
+    /**
      * (AsyncResult)response.obj).result is an int[] with element [0] set to
      * 1 for "CLIP is provisioned", and 0 for "CLIP is not provisioned".
      *
diff --git a/telephony/java/com/android/internal/telephony/IccCardStatus.java b/telephony/java/com/android/internal/telephony/IccCardStatus.java
index c751a21..a3bdd76 100644
--- a/telephony/java/com/android/internal/telephony/IccCardStatus.java
+++ b/telephony/java/com/android/internal/telephony/IccCardStatus.java
@@ -24,7 +24,7 @@
  * {@hide}
  */
 public class IccCardStatus {
-    static final int CARD_MAX_APPS = 8;
+    public static final int CARD_MAX_APPS = 8;
 
     public enum CardState {
         CARDSTATE_ABSENT,
diff --git a/telephony/java/com/android/internal/telephony/IccFileHandler.java b/telephony/java/com/android/internal/telephony/IccFileHandler.java
index 93b9b79..380bfd1 100644
--- a/telephony/java/com/android/internal/telephony/IccFileHandler.java
+++ b/telephony/java/com/android/internal/telephony/IccFileHandler.java
@@ -145,8 +145,9 @@
             = obtainMessage(EVENT_GET_RECORD_SIZE_DONE,
                         new LoadLinearFixedContext(fileid, recordNum, onLoaded));
 
-        phone.mCM.iccIO(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
-                        0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response);
+        phone.mCM.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
+                        0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null,
+                        phone.getIccCard().getAid(), response);
     }
 
     /**
@@ -164,9 +165,10 @@
                         onLoaded));
 
         // TODO(): Verify when path changes are done.
-        phone.mCM.iccIO(COMMAND_GET_RESPONSE, IccConstants.EF_IMG, "img",
+        phone.mCM.iccIOForApp(COMMAND_GET_RESPONSE, IccConstants.EF_IMG, "img",
                 recordNum, READ_RECORD_MODE_ABSOLUTE,
-                GET_RESPONSE_EF_IMG_SIZE_BYTES, null, null, response);
+                GET_RESPONSE_EF_IMG_SIZE_BYTES, null, null,
+                phone.getIccCard().getAid(), response);
     }
 
     /**
@@ -182,8 +184,9 @@
         Message response
                 = obtainMessage(EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE,
                         new LoadLinearFixedContext(fileid, onLoaded));
-        phone.mCM.iccIO(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
-                    0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response);
+        phone.mCM.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
+                    0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, phone.getIccCard().getAid(),
+                    response);
     }
 
     /**
@@ -199,8 +202,9 @@
         Message response = obtainMessage(EVENT_GET_RECORD_SIZE_DONE,
                         new LoadLinearFixedContext(fileid,onLoaded));
 
-        phone.mCM.iccIO(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
-                        0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response);
+        phone.mCM.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
+                        0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null,
+                        phone.getIccCard().getAid(), response);
     }
 
     /**
@@ -217,8 +221,9 @@
         Message response = obtainMessage(EVENT_GET_BINARY_SIZE_DONE,
                         fileid, 0, onLoaded);
 
-        phone.mCM.iccIO(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
-                        0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response);
+        phone.mCM.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
+                        0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null,
+                        phone.getIccCard().getAid(), response);
     }
 
     /**
@@ -236,8 +241,8 @@
         Message response = obtainMessage(EVENT_READ_ICON_DONE, fileid, 0,
                 onLoaded);
 
-        phone.mCM.iccIO(COMMAND_READ_BINARY, fileid, "img", highOffset, lowOffset,
-                length, null, null, response);
+        phone.mCM.iccIOForApp(COMMAND_READ_BINARY, fileid, "img", highOffset, lowOffset,
+                length, null, null, phone.getIccCard().getAid(), response);
     }
 
     /**
@@ -251,9 +256,10 @@
      */
     public void updateEFLinearFixed(int fileid, int recordNum, byte[] data,
             String pin2, Message onComplete) {
-        phone.mCM.iccIO(COMMAND_UPDATE_RECORD, fileid, getEFPath(fileid),
+        phone.mCM.iccIOForApp(COMMAND_UPDATE_RECORD, fileid, getEFPath(fileid),
                         recordNum, READ_RECORD_MODE_ABSOLUTE, data.length,
-                        IccUtils.bytesToHexString(data), pin2, onComplete);
+                        IccUtils.bytesToHexString(data), pin2,
+                        phone.getIccCard().getAid(), onComplete);
     }
 
     /**
@@ -262,9 +268,10 @@
      * @param data must be exactly as long as the EF
      */
     public void updateEFTransparent(int fileid, byte[] data, Message onComplete) {
-        phone.mCM.iccIO(COMMAND_UPDATE_BINARY, fileid, getEFPath(fileid),
+        phone.mCM.iccIOForApp(COMMAND_UPDATE_BINARY, fileid, getEFPath(fileid),
                         0, 0, data.length,
-                        IccUtils.bytesToHexString(data), null, onComplete);
+                        IccUtils.bytesToHexString(data), null,
+                        phone.getIccCard().getAid(), onComplete);
     }
 
 
@@ -395,10 +402,11 @@
                      lc.results = new ArrayList<byte[]>(lc.countRecords);
                  }
 
-                 phone.mCM.iccIO(COMMAND_READ_RECORD, lc.efid, getEFPath(lc.efid),
+                 phone.mCM.iccIOForApp(COMMAND_READ_RECORD, lc.efid, getEFPath(lc.efid),
                          lc.recordNum,
                          READ_RECORD_MODE_ABSOLUTE,
                          lc.recordSize, null, null,
+                         phone.getIccCard().getAid(),
                          obtainMessage(EVENT_READ_RECORD_DONE, lc));
                  break;
             case EVENT_GET_BINARY_SIZE_DONE:
@@ -433,8 +441,9 @@
                 size = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8)
                        + (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff);
 
-                phone.mCM.iccIO(COMMAND_READ_BINARY, fileid, getEFPath(fileid),
+                phone.mCM.iccIOForApp(COMMAND_READ_BINARY, fileid, getEFPath(fileid),
                                 0, 0, size, null, null,
+                                phone.getIccCard().getAid(),
                                 obtainMessage(EVENT_READ_BINARY_DONE,
                                               fileid, 0, response));
             break;
@@ -468,10 +477,11 @@
                     if (lc.recordNum > lc.countRecords) {
                         sendResult(response, lc.results, null);
                     } else {
-                        phone.mCM.iccIO(COMMAND_READ_RECORD, lc.efid, getEFPath(lc.efid),
+                        phone.mCM.iccIOForApp(COMMAND_READ_RECORD, lc.efid, getEFPath(lc.efid),
                                     lc.recordNum,
                                     READ_RECORD_MODE_ABSOLUTE,
                                     lc.recordSize, null, null,
+                                    phone.getIccCard().getAid(),
                                     obtainMessage(EVENT_READ_RECORD_DONE, lc));
                     }
                 }
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 156eb32..f587fe1 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -880,9 +880,19 @@
 
     public void
     getIMSI(Message result) {
+        getIMSIForApp(null, result);
+    }
+
+    public void
+    getIMSIForApp(String aid, Message result) {
         RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMSI, result);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+        rr.mp.writeInt(1);
+        rr.mp.writeString(aid);
+
+        if (RILJ_LOGD) riljLog(rr.serialString() +
+                              "> getIMSI: " + requestToString(rr.mRequest)
+                              + " aid: " + aid);
 
         send(rr);
     }
@@ -1435,6 +1445,11 @@
     public void
     iccIO (int command, int fileid, String path, int p1, int p2, int p3,
             String data, String pin2, Message result) {
+        iccIOForApp(command, fileid, path, p1, p2, p3, data, pin2, null, result);
+    }
+    public void
+    iccIOForApp (int command, int fileid, String path, int p1, int p2, int p3,
+            String data, String pin2, String aid, Message result) {
         //Note: This RIL request has not been renamed to ICC,
         //       but this request is also valid for SIM and RUIM
         RILRequest rr
@@ -1448,12 +1463,15 @@
         rr.mp.writeInt(p3);
         rr.mp.writeString(data);
         rr.mp.writeString(pin2);
+        rr.mp.writeString(aid);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> iccIO: " + requestToString(rr.mRequest)
+        if (RILJ_LOGD) riljLog(rr.serialString() + "> iccIO: "
+                + requestToString(rr.mRequest)
                 + " 0x" + Integer.toHexString(command)
                 + " 0x" + Integer.toHexString(fileid) + " "
                 + " path: " + path + ","
-                + p1 + "," + p2 + "," + p3);
+                + p1 + "," + p2 + "," + p3
+                + " aid: " + aid);
 
         send(rr);
     }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java
index b57af0e..8375fd0 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java
@@ -55,8 +55,8 @@
         if (fileid == EF_CSIM_EPRL) {
             // Entire PRL could be huge. We are only interested in
             // the first 4 bytes of the record.
-            phone.mCM.iccIO(COMMAND_READ_BINARY, fileid, getEFPath(fileid),
-                            0, 0, 4, null, null,
+            phone.mCM.iccIOForApp(COMMAND_READ_BINARY, fileid, getEFPath(fileid),
+                            0, 0, 4, null, null, phone.getIccCard().getAid(),
                             obtainMessage(EVENT_READ_BINARY_DONE,
                                           fileid, 0, onLoaded));
         } else {
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java b/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java
index 3e2a29b..375cc07 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java
@@ -57,8 +57,9 @@
         Message response = obtainMessage(EVENT_READ_ICON_DONE, fileid, 0,
                 onLoaded);
 
-        phone.mCM.iccIO(COMMAND_GET_RESPONSE, fileid, "img", 0, 0,
-                GET_RESPONSE_EF_IMG_SIZE_BYTES, null, null, response);
+        phone.mCM.iccIOForApp(COMMAND_GET_RESPONSE, fileid, "img", 0, 0,
+                GET_RESPONSE_EF_IMG_SIZE_BYTES, null, null,
+                phone.getIccCard().getAid(), response);
     }
 
     @Override
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
index b2fa051..de8401e 100755
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -1309,7 +1309,7 @@
 
         logv("fetchSimRecords " + recordsToLoad);
 
-        phone.mCM.getIMSI(obtainMessage(EVENT_GET_IMSI_DONE));
+        phone.mCM.getIMSIForApp(phone.getIccCard().getAid(), obtainMessage(EVENT_GET_IMSI_DONE));
         recordsToLoad++;
 
         iccFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
diff --git a/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
index 9201984..99f4e0f 100644
--- a/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -84,6 +84,9 @@
     public void getIMSI(Message result) {
     }
 
+    public void getIMSIForApp(String aid, Message result) {
+    }
+
     public void getIMEI(Message result) {
     }
 
@@ -213,6 +216,9 @@
     public void iccIO (int command, int fileid, String path, int p1, int p2,
             int p3, String data, String pin2, Message result) {
     }
+    public void iccIOForApp (int command, int fileid, String path, int p1, int p2,
+            int p3, String data, String pin2, String aid, Message result) {
+    }
 
     public void getCLIR(Message result) {
     }
diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
index 60d9d24..4f61509 100644
--- a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -504,6 +504,9 @@
         resultSuccess(result, null);
     }
 
+    public void getIMSI(Message result) {
+        getIMSIForApp(null, result);
+    }
     /**
      *  returned message
      *  retMsg.obj = AsyncResult ar
@@ -511,7 +514,7 @@
      *  ar.userObject contains the original value of result.obj
      *  ar.result is String containing IMSI on success
      */
-    public void getIMSI(Message result) {
+    public void getIMSIForApp(String aid, Message result) {
         resultSuccess(result, "012345678901234");
     }
 
@@ -1042,13 +1045,18 @@
         unimplemented(result);
     }
 
+    public void iccIO(int command, int fileid, String path, int p1, int p2, int p3, String data,
+            String pin2, Message response) {
+        iccIOForApp(command, fileid, path, p1, p2, p3, data,pin2, null, response);
+    }
+
     /**
      * parameters equivalent to 27.007 AT+CRSM command
      * response.obj will be an AsyncResult
      * response.obj.userObj will be a SimIoResult on success
      */
-    public void iccIO (int command, int fileid, String path, int p1, int p2,
-                       int p3, String data, String pin2, Message result) {
+    public void iccIOForApp (int command, int fileid, String path, int p1, int p2,
+                       int p3, String data, String pin2, String aid, Message result) {
         unimplemented(result);
     }
 
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java
index b385cee..ea6836d 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java
@@ -612,4 +612,13 @@
     @Override
     public void getVoiceRadioTechnology(Message response) {
     }
+
+    @Override
+    public void getIMSIForApp(String aid, Message result) {
+    }
+
+    @Override
+    public void iccIOForApp(int command, int fileid, String path, int p1, int p2, int p3,
+            String data, String pin2, String aid, Message response) {
+    }
 }
diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-runner/src/android/test/mock/MockContentProvider.java
index 4ff943e..a8c388e 100644
--- a/test-runner/src/android/test/mock/MockContentProvider.java
+++ b/test-runner/src/android/test/mock/MockContentProvider.java
@@ -21,7 +21,7 @@
 import android.content.ContentProviderResult;
 import android.content.ContentValues;
 import android.content.Context;
-import android.content.ICancelationSignal;
+import android.content.ICancellationSignal;
 import android.content.IContentProvider;
 import android.content.OperationApplicationException;
 import android.content.pm.PathPermission;
@@ -93,7 +93,7 @@
 
         @Override
         public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
-                String sortOrder, ICancelationSignal cancelationSignal) throws RemoteException {
+                String sortOrder, ICancellationSignal cancellationSignal) throws RemoteException {
             return MockContentProvider.this.query(url, projection, selection,
                     selectionArgs, sortOrder);
         }
@@ -127,7 +127,7 @@
         }
 
         @Override
-        public ICancelationSignal createCancelationSignal() throws RemoteException {
+        public ICancellationSignal createCancellationSignal() throws RemoteException {
             return null;
         }
     }
diff --git a/test-runner/src/android/test/mock/MockIContentProvider.java b/test-runner/src/android/test/mock/MockIContentProvider.java
index 41bc27d..1aa0448 100644
--- a/test-runner/src/android/test/mock/MockIContentProvider.java
+++ b/test-runner/src/android/test/mock/MockIContentProvider.java
@@ -21,7 +21,7 @@
 import android.content.ContentValues;
 import android.content.EntityIterator;
 import android.content.IContentProvider;
-import android.content.ICancelationSignal;
+import android.content.ICancellationSignal;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.net.Uri;
@@ -73,7 +73,7 @@
     }
 
     public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
-            String sortOrder, ICancelationSignal cancelationSignal) {
+            String sortOrder, ICancellationSignal cancellationSignal) {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
@@ -106,7 +106,7 @@
     }
 
     @Override
-    public ICancelationSignal createCancelationSignal() throws RemoteException {
+    public ICancellationSignal createCancellationSignal() throws RemoteException {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 }
diff --git a/tests/BiDiTests/res/layout/basic.xml b/tests/BiDiTests/res/layout/basic.xml
index f503658..7d4d4db 100644
--- a/tests/BiDiTests/res/layout/basic.xml
+++ b/tests/BiDiTests/res/layout/basic.xml
@@ -19,6 +19,10 @@
         android:layout_width="fill_parent"
         android:layout_height="fill_parent">
 
+    <ScrollView
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent">
+
     <LinearLayout android:orientation="vertical"
                   android:layout_width="match_parent"
                   android:layout_height="match_parent">
@@ -131,4 +135,6 @@
 
     </LinearLayout>
 
+    </ScrollView>
+
 </FrameLayout>
diff --git a/tests/BiDiTests/res/layout/grid_layout_locale.xml b/tests/BiDiTests/res/layout/grid_layout_locale.xml
new file mode 100644
index 0000000..4198898
--- /dev/null
+++ b/tests/BiDiTests/res/layout/grid_layout_locale.xml
@@ -0,0 +1,74 @@
+<?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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/grid_layout_locale"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent">
+
+
+    <GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:useDefaultMargins="true"
+            android:alignmentMode="alignBounds"
+            android:columnOrderPreserved="false"
+            android:columnCount="4"
+            android:layoutDirection="locale">
+
+        <TextView
+                android:text="Email setup"
+                android:textSize="32dip"
+
+                android:layout_columnSpan="4"
+                android:layout_gravity="center_horizontal"/>
+
+        <TextView
+                android:text="You can configure email in just a few steps:"
+                android:textSize="16dip"
+                android:layout_columnSpan="4"
+                android:layout_gravity="left"/>
+
+        <TextView
+                android:text="Email address:"
+                android:layout_gravity="right"/>
+
+        <EditText
+                android:ems="10"/>
+
+        <TextView
+                android:text="Password:"
+                android:layout_column="0"
+                android:layout_gravity="right"/>
+
+        <EditText
+                android:ems="8"/>
+
+        <Space
+                android:layout_row="4"
+                android:layout_column="0"
+                android:layout_columnSpan="3"
+                android:layout_gravity="fill"
+                />
+
+        <Button
+                android:text="Next"
+                android:layout_row="5"
+                android:layout_column="3"/>
+
+    </GridLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/tests/BiDiTests/res/layout/grid_layout_ltr.xml b/tests/BiDiTests/res/layout/grid_layout_ltr.xml
new file mode 100644
index 0000000..46ea658
--- /dev/null
+++ b/tests/BiDiTests/res/layout/grid_layout_ltr.xml
@@ -0,0 +1,74 @@
+<?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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/grid_layout_ltr"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent">
+
+
+    <GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:useDefaultMargins="true"
+            android:alignmentMode="alignBounds"
+            android:columnOrderPreserved="false"
+            android:columnCount="4"
+            android:layoutDirection="ltr">
+
+        <TextView
+                android:text="Email setup"
+                android:textSize="32dip"
+
+                android:layout_columnSpan="4"
+                android:layout_gravity="center_horizontal"/>
+
+        <TextView
+                android:text="You can configure email in just a few steps:"
+                android:textSize="16dip"
+                android:layout_columnSpan="4"
+                android:layout_gravity="left"/>
+
+        <TextView
+                android:text="Email address:"
+                android:layout_gravity="right"/>
+
+        <EditText
+                android:ems="10"/>
+
+        <TextView
+                android:text="Password:"
+                android:layout_column="0"
+                android:layout_gravity="right"/>
+
+        <EditText
+                android:ems="8"/>
+
+        <Space
+                android:layout_row="4"
+                android:layout_column="0"
+                android:layout_columnSpan="3"
+                android:layout_gravity="fill"
+                />
+
+        <Button
+                android:text="Next"
+                android:layout_row="5"
+                android:layout_column="3"/>
+
+    </GridLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/tests/BiDiTests/res/layout/grid_layout_rtl.xml b/tests/BiDiTests/res/layout/grid_layout_rtl.xml
new file mode 100644
index 0000000..947e13c
--- /dev/null
+++ b/tests/BiDiTests/res/layout/grid_layout_rtl.xml
@@ -0,0 +1,74 @@
+<?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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/grid_layout_rtl"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent">
+
+
+    <GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:useDefaultMargins="true"
+            android:alignmentMode="alignBounds"
+            android:columnOrderPreserved="false"
+            android:columnCount="4"
+            android:layoutDirection="rtl">
+
+        <TextView
+                android:text="Email setup"
+                android:textSize="32dip"
+
+                android:layout_columnSpan="4"
+                android:layout_gravity="center_horizontal"/>
+
+        <TextView
+                android:text="You can configure email in just a few steps:"
+                android:textSize="16dip"
+                android:layout_columnSpan="4"
+                android:layout_gravity="left"/>
+
+        <TextView
+                android:text="Email address:"
+                android:layout_gravity="right"/>
+
+        <EditText
+                android:ems="10"/>
+
+        <TextView
+                android:text="Password:"
+                android:layout_column="0"
+                android:layout_gravity="right"/>
+
+        <EditText
+                android:ems="8"/>
+
+        <Space
+                android:layout_row="4"
+                android:layout_column="0"
+                android:layout_columnSpan="3"
+                android:layout_gravity="fill"
+                />
+
+        <Button
+                android:text="Next"
+                android:layout_row="5"
+                android:layout_column="3"/>
+
+    </GridLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/tests/BiDiTests/res/values/strings.xml b/tests/BiDiTests/res/values/strings.xml
index 1d4fc84..233cd0d 100644
--- a/tests/BiDiTests/res/values/strings.xml
+++ b/tests/BiDiTests/res/values/strings.xml
@@ -24,11 +24,11 @@
     <string name="button_before_text">Start</string>
     <string name="button_requestlayout_text">Request Layout</string>
     <string name="button_alert_dialog_text">AlertDialog</string>
-    <string name="textview_text">This is a text for a TextView</string>
-    <string name="textview_ltr_text">This is a text for a LTR TextView</string>
-    <string name="textview_rtl_text">This is a text for a RTL TextView</string>
-    <string name="textview_default_text">This is a text for a default TextView</string>
-    <string name="textview_password_default_text">This is a text for a password TextView</string>
+    <string name="textview_text">TextView</string>
+    <string name="textview_ltr_text">LTR TextView</string>
+    <string name="textview_rtl_text">RTL TextView</string>
+    <string name="textview_default_text">Default TextView</string>
+    <string name="textview_password_default_text">Password TextView</string>
     <string name="edittext_text">mmmmmmmmmmmmmmmmmmmmmmmm</string>
     <string name="normal_text">Normal String</string>
     <string name="normal_long_text">mmmmmmmmmmmmmmmmmmmmmmmm</string>
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
index b45b98f..4d7ace1 100644
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
@@ -108,6 +108,10 @@
         addItem(result, "Linear RTL", BiDiTestLinearLayoutRtl.class, R.id.linear_layout_rtl);
         addItem(result, "Linear LOC", BiDiTestLinearLayoutLocale.class, R.id.linear_layout_locale);
 
+        addItem(result, "Grid LTR", BiDiTestGridLayoutLtr.class, R.id.grid_layout_ltr);
+        addItem(result, "Grid RTL", BiDiTestGridLayoutRtl.class, R.id.grid_layout_rtl);
+        addItem(result, "Grid LOC", BiDiTestGridLayoutLocale.class, R.id.grid_layout_locale);
+
         addItem(result, "Frame LTR", BiDiTestFrameLayoutLtr.class, R.id.frame_layout_ltr);
         addItem(result, "Frame RTL", BiDiTestFrameLayoutRtl.class, R.id.frame_layout_rtl);
         addItem(result, "Frame LOC", BiDiTestFrameLayoutLocale.class, R.id.frame_layout_locale);
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutLocale.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutLocale.java
new file mode 100644
index 0000000..16e61ad
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutLocale.java
@@ -0,0 +1,32 @@
+/*
+ * 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.bidi;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class BiDiTestGridLayoutLocale extends Fragment {
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.grid_layout_locale, container, false);
+    }
+}
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutLtr.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutLtr.java
new file mode 100644
index 0000000..df6c9fe
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutLtr.java
@@ -0,0 +1,32 @@
+/*
+ * 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.bidi;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class BiDiTestGridLayoutLtr extends Fragment {
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.grid_layout_ltr, container, false);
+    }
+}
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutRtl.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutRtl.java
new file mode 100644
index 0000000..8bed113
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestGridLayoutRtl.java
@@ -0,0 +1,32 @@
+/*
+ * 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.bidi;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class BiDiTestGridLayoutRtl extends Fragment {
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.grid_layout_rtl, container, false);
+    }
+}
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
index a706f78..a38ac25 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
@@ -40,8 +40,11 @@
     private long mContentInvalMillis;
     private static final int LOAD_STALL_MILLIS = 2000; // nr of millis after load,
                                                        // before test is forced
-    private double mLoadTime;
-    private double mAnimationTime;
+
+    // ignore anim end events until this many millis after load
+    private static final long ANIM_SAFETY_THRESHOLD = 200;
+    private long mLoadTime;
+    private long mAnimationTime;
 
     public ProfiledWebView(Context context) {
         super(context);
@@ -131,7 +134,6 @@
                     // invalidate all content, and kick off redraw
                     Log.d("ProfiledWebView",
                             "kicking off test with callback registration, and tile discard...");
-                    registerPageSwapCallback();
                     discardAllTextures();
                     invalidate();
                     mIsScrolling = true;
@@ -150,10 +152,11 @@
      */
     @Override
     protected void pageSwapCallback(boolean startAnim) {
+        super.pageSwapCallback(startAnim);
+
         if (!mIsTesting && mIsScrolling) {
             // kick off testing
             mContentInvalMillis = System.currentTimeMillis() - mContentInvalMillis;
-            super.pageSwapCallback(startAnim);
             Log.d("ProfiledWebView", "REDRAW TOOK " + mContentInvalMillis + "millis");
             mIsTesting = true;
             invalidate(); // ensure a redraw so that auto-scrolling can occur
@@ -166,14 +169,14 @@
         String updatesString = settings.getProperty("tree_updates");
         int updates = (updatesString == null) ? -1 : Integer.parseInt(updatesString);
 
-        double animationTime;
-        if (mAnimationTime == 0) {
+        long animationTime;
+        if (mAnimationTime == 0 || mAnimationTime - mLoadTime < ANIM_SAFETY_THRESHOLD) {
             animationTime = System.currentTimeMillis() - mLoadTime;
         } else {
             animationTime = mAnimationTime - mLoadTime;
         }
 
-        return updates * 1000 / animationTime;
+        return updates * 1000.0 / animationTime;
     }
 
     public void setDoubleBuffering(boolean useDoubleBuffering) {
diff --git a/tests/touchlag/Android.mk b/tests/touchlag/Android.mk
new file mode 100644
index 0000000..4f8aa1e
--- /dev/null
+++ b/tests/touchlag/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	touchlag.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils libutils \
+
+LOCAL_MODULE:= test-touchlag
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/tests/touchlag/touchlag.cpp b/tests/touchlag/touchlag.cpp
new file mode 100644
index 0000000..df4befb
--- /dev/null
+++ b/tests/touchlag/touchlag.cpp
@@ -0,0 +1,294 @@
+/*
+ * 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.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/fb.h>
+#include <linux/input.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <cutils/memory.h>
+#include <asm-generic/mman.h>
+#include <sys/mman.h>
+#include <utils/threads.h>
+#include <unistd.h>
+#include <math.h>
+
+using namespace android;
+
+#ifndef FBIO_WAITFORVSYNC
+#define FBIO_WAITFORVSYNC   _IOW('F', 0x20, __u32)
+#endif
+
+struct Buffer {
+    size_t w;
+    size_t h;
+    size_t s;
+    union {
+        void* addr;
+        uint32_t* pixels;
+    };
+};
+
+void clearBuffer(Buffer* buf, uint32_t pixel) {
+    android_memset32(buf->pixels, pixel, buf->s * buf->h * 4);
+}
+
+void drawTwoPixels(Buffer* buf, uint32_t pixel, ssize_t x, ssize_t y, size_t w) {
+    if (y>0 && y<ssize_t(buf->h)) {
+        uint32_t* bits = buf->pixels + y * buf->s;
+        if (x>=0 && x<buf->w) {
+            bits[x] = pixel;
+        }
+        ssize_t W(w);
+        if ((x+W)>=0 && (x+W)<buf->w) {
+            bits[x+W] = pixel;
+        }
+    }
+}
+
+void drawHLine(Buffer* buf, uint32_t pixel, ssize_t x, ssize_t y, size_t w) {
+    if (y>0 && y<ssize_t(buf->h)) {
+        ssize_t W(w);
+        if (x<0) {
+            W += x;
+            x = 0;
+        }
+        if (x+w > buf->w) {
+            W = buf->w - x;
+        }
+        if (W>0) {
+            uint32_t* bits = buf->pixels + y * buf->s + x;
+            android_memset32(bits, pixel, W*4);
+        }
+    }
+}
+
+void drawRect(Buffer* buf, uint32_t pixel, ssize_t x, ssize_t y, size_t w, size_t h) {
+    ssize_t W(w), H(h);
+    if (x<0) {
+        w += x;
+        x = 0;
+    }
+    if (y<0) {
+        h += y;
+        y = 0;
+    }
+    if (x+w > buf->w)   W = buf->w - x;
+    if (y+h > buf->h)   H = buf->h - y;
+    if (W>0 && H>0) {
+        uint32_t* bits = buf->pixels + y * buf->s + x;
+        for (ssize_t i=0 ; i<H ; i++) {
+            android_memset32(bits, pixel, W*4);
+            bits += buf->s;
+        }
+    }
+}
+
+void drawCircle(Buffer* buf, uint32_t pixel,
+        size_t x0, size_t y0, size_t radius, bool filled = false) {
+    ssize_t f = 1 - radius;
+    ssize_t ddF_x = 1;
+    ssize_t ddF_y = -2 * radius;
+    ssize_t x = 0;
+    ssize_t y = radius;
+    if (filled) {
+        drawHLine(buf, pixel, x0-radius, y0, 2*radius);
+    } else {
+        drawTwoPixels(buf, pixel, x0-radius, y0, 2*radius);
+    }
+    while (x < y) {
+        if (f >= 0) {
+            y--;
+            ddF_y += 2;
+            f += ddF_y;
+        }
+        x++;
+        ddF_x += 2;
+        f += ddF_x;
+        if (filled) {
+            drawHLine(buf, pixel, x0-x, y0+y, 2*x);
+            drawHLine(buf, pixel, x0-x, y0-y, 2*x);
+            drawHLine(buf, pixel, x0-y, y0+x, 2*y);
+            drawHLine(buf, pixel, x0-y, y0-x, 2*y);
+        } else {
+            drawTwoPixels(buf, pixel, x0-x, y0+y, 2*x);
+            drawTwoPixels(buf, pixel, x0-x, y0-y, 2*x);
+            drawTwoPixels(buf, pixel, x0-y, y0+x, 2*y);
+            drawTwoPixels(buf, pixel, x0-y, y0-x, 2*y);
+        }
+    }
+}
+
+class TouchEvents {
+    class EventThread : public Thread {
+        int fd;
+
+        virtual bool threadLoop() {
+            input_event event;
+            int first_down = 0;
+            do {
+                read(fd, &event, sizeof(event));
+                if (event.type == EV_ABS) {
+                    if (event.code == ABS_MT_TRACKING_ID) {
+                        down = event.value == -1 ? 0 : 1;
+                        first_down = down;
+                    }
+                    if (event.code == ABS_MT_POSITION_X) {
+                        x = event.value;
+                    }
+                    if (event.code == ABS_MT_POSITION_Y) {
+                        y = event.value;
+                    }
+                }
+            } while (event.type == EV_SYN);
+            return true;
+        }
+
+    public:
+        int x, y, down;
+        EventThread() : Thread(false),
+                x(0), y(0), down(0)
+        {
+            fd = open("/dev/input/event1", O_RDONLY);
+        }
+};
+    sp<EventThread> thread;
+
+public:
+    TouchEvents() {
+        thread = new EventThread();
+        thread->run("EventThread", PRIORITY_URGENT_DISPLAY);
+    }
+
+    int getMostRecentPosition(int* x, int* y) {
+        *x = thread->x;
+        *y = thread->y;
+        return thread->down;
+    }
+};
+
+
+struct Queue {
+    struct position {
+        int x, y;
+    };
+    int index;
+    position q[16];
+    Queue() : index(0) { }
+    void push(int x, int y) {
+        index++;
+        index &= 0xF;
+        q[index].x = x;
+        q[index].y = y;
+    }
+    void get(int lag, int* x, int* y) {
+        const int i = (index - lag) & 0xF;
+        *x = q[i].x;
+        *y = q[i].y;
+    }
+};
+
+extern char *optarg;
+extern int optind;
+extern int optopt;
+extern int opterr;
+extern int optreset;
+
+void usage(const char* name) {
+    printf("\nusage: %s [-h] [-l lag]\n", name);
+}
+
+int main(int argc, char** argv) {
+    fb_var_screeninfo vi;
+    fb_fix_screeninfo fi;
+
+    int lag = 0;
+    int fd = open("/dev/graphics/fb0", O_RDWR);
+    ioctl(fd, FBIOGET_VSCREENINFO, &vi);
+    ioctl(fd, FBIOGET_FSCREENINFO, &fi);
+    void* bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+    Buffer framebuffer;
+    framebuffer.w = vi.xres;
+    framebuffer.h = vi.yres;
+    framebuffer.s = fi.line_length / (vi.bits_per_pixel >> 3);
+    framebuffer.addr = bits;
+
+    int ch;
+    while ((ch = getopt(argc, argv, "hl:")) != -1) {
+        switch (ch) {
+            case 'l':
+                lag = atoi(optarg);
+                break;
+            case 'h':
+            default:
+                usage(argv[0]);
+                exit(0);
+        }
+    }
+    argc -= optind;
+    argv += optind;
+
+
+    TouchEvents touch;
+    Queue queue;
+
+
+    int x=0, y=0, down=0;
+    int lag_x=0, lag_y=0;
+
+    clearBuffer(&framebuffer, 0);
+    while (true) {
+        uint32_t crt = 0;
+        int err = ioctl(fd, FBIO_WAITFORVSYNC, &crt);
+
+        // draw beam marker
+        drawRect(&framebuffer, 0x400000, framebuffer.w-2, 0, 2, framebuffer.h);
+        // erase screen
+        if (lag) {
+            drawCircle(&framebuffer, 0, lag_x, lag_y, 100);
+            drawHLine(&framebuffer, 0, 0, lag_y, 32);
+        }
+        drawCircle(&framebuffer, 0, x, y, 100, true);
+        drawHLine(&framebuffer, 0, 0, y, 32);
+
+        // draw a line at y=1000
+        drawHLine(&framebuffer, 0x808080, 0, 1000, framebuffer.w);
+
+        // get touch events
+        touch.getMostRecentPosition(&x, &y);
+        queue.push(x, y);
+        queue.get(lag, &lag_x, &lag_y);
+
+        if (lag) {
+            drawCircle(&framebuffer, 0x00FF00, lag_x, lag_y, 100);
+            drawHLine(&framebuffer, 0x00FF00, 0, lag_y, 32);
+        }
+
+        drawCircle(&framebuffer, 0xFFFFFF, x, y, 100, true);
+        drawHLine(&framebuffer, 0xFFFFFF, 0, y, 32);
+
+        // draw end of frame beam marker
+        drawRect(&framebuffer, 0x004000, framebuffer.w-2, 0, 2, framebuffer.h);
+    }
+
+    close(fd);
+    return 0;
+}
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index c0d7427..95a68d1 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -45,6 +45,7 @@
 
 static const String16 RESOURCES_PREFIX(RESOURCES_ROOT_NAMESPACE);
 static const String16 RESOURCES_PRV_PREFIX(RESOURCES_ROOT_PRV_NAMESPACE);
+static const String16 RESOURCES_TOOLS_NAMESPACE("http://schemas.android.com/tools");
 
 String16 getNamespaceResourcePackage(String16 namespaceUri, bool* outIsPublic)
 {
@@ -761,13 +762,16 @@
         SourcePos(mFilename, getStartLineNumber()).error("Child to CDATA node.");
         return UNKNOWN_ERROR;
     }
-    attribute_entry e;
-    e.index = mNextAttributeIndex++;
-    e.ns = ns;
-    e.name = name;
-    e.string = value;
-    mAttributes.add(e);
-    mAttributeOrder.add(e.index, mAttributes.size()-1);
+
+    if (ns != RESOURCES_TOOLS_NAMESPACE) {
+        attribute_entry e;
+        e.index = mNextAttributeIndex++;
+        e.ns = ns;
+        e.name = name;
+        e.string = value;
+        mAttributes.add(e);
+        mAttributeOrder.add(e.index, mAttributes.size()-1);
+    }
     return NO_ERROR;
 }
 
@@ -1215,11 +1219,13 @@
     collect_attr_strings(dest, outResIds, true);
     
     int i;
-    if (mNamespacePrefix.size() > 0) {
-        dest->add(mNamespacePrefix, true);
-    }
-    if (mNamespaceUri.size() > 0) {
-        dest->add(mNamespaceUri, true);
+    if (RESOURCES_TOOLS_NAMESPACE != mNamespaceUri) {
+        if (mNamespacePrefix.size() > 0) {
+            dest->add(mNamespacePrefix, true);
+        }
+        if (mNamespaceUri.size() > 0) {
+            dest->add(mNamespaceUri, true);
+        }
     }
     if (mElementName.size() > 0) {
         dest->add(mElementName, true);
@@ -1338,6 +1344,7 @@
     const void* extData = NULL;
     size_t extSize = 0;
     ResXMLTree_attribute attr;
+    bool writeCurrentNode = true;
 
     const size_t NA = mAttributes.size();
     const size_t NC = mChildren.size();
@@ -1350,7 +1357,7 @@
     const String16 style16("style");
 
     const type type = getType();
-    
+
     memset(&node, 0, sizeof(node));
     memset(&attr, 0, sizeof(attr));
     node.header.headerSize = htods(sizeof(node));
@@ -1395,17 +1402,21 @@
             }
         }
     } else if (type == TYPE_NAMESPACE) {
-        node.header.type = htods(RES_XML_START_NAMESPACE_TYPE);
-        extData = &namespaceExt;
-        extSize = sizeof(namespaceExt);
-        memset(&namespaceExt, 0, sizeof(namespaceExt));
-        if (mNamespacePrefix.size() > 0) {
-            namespaceExt.prefix.index = htodl(strings.offsetForString(mNamespacePrefix));
+        if (mNamespaceUri == RESOURCES_TOOLS_NAMESPACE) {
+            writeCurrentNode = false;
         } else {
-            namespaceExt.prefix.index = htodl((uint32_t)-1);
+            node.header.type = htods(RES_XML_START_NAMESPACE_TYPE);
+            extData = &namespaceExt;
+            extSize = sizeof(namespaceExt);
+            memset(&namespaceExt, 0, sizeof(namespaceExt));
+            if (mNamespacePrefix.size() > 0) {
+                namespaceExt.prefix.index = htodl(strings.offsetForString(mNamespacePrefix));
+            } else {
+                namespaceExt.prefix.index = htodl((uint32_t)-1);
+            }
+            namespaceExt.prefix.index = htodl(strings.offsetForString(mNamespacePrefix));
+            namespaceExt.uri.index = htodl(strings.offsetForString(mNamespaceUri));
         }
-        namespaceExt.prefix.index = htodl(strings.offsetForString(mNamespacePrefix));
-        namespaceExt.uri.index = htodl(strings.offsetForString(mNamespaceUri));
         LOG_ALWAYS_FATAL_IF(NA != 0, "Namespace nodes can't have attributes!");
     } else if (type == TYPE_CDATA) {
         node.header.type = htods(RES_XML_CDATA_TYPE);
@@ -1422,9 +1433,11 @@
 
     node.header.size = htodl(sizeof(node) + extSize + (sizeof(attr)*NA));
 
-    dest->writeData(&node, sizeof(node));
-    if (extSize > 0) {
-        dest->writeData(extData, extSize);
+    if (writeCurrentNode) {
+        dest->writeData(&node, sizeof(node));
+        if (extSize > 0) {
+            dest->writeData(extData, extSize);
+        }
     }
 
     for (i=0; i<NA; i++) {
@@ -1476,12 +1489,14 @@
         dest->writeData(&node, sizeof(node));
         dest->writeData(&endElementExt, sizeof(endElementExt));
     } else if (type == TYPE_NAMESPACE) {
-        node.header.type = htods(RES_XML_END_NAMESPACE_TYPE);
-        node.lineNumber = htodl(getEndLineNumber());
-        node.comment.index = htodl((uint32_t)-1);
-        node.header.size = htodl(sizeof(node)+extSize);
-        dest->writeData(&node, sizeof(node));
-        dest->writeData(extData, extSize);
+        if (writeCurrentNode) {
+            node.header.type = htods(RES_XML_END_NAMESPACE_TYPE);
+            node.lineNumber = htodl(getEndLineNumber());
+            node.comment.index = htodl((uint32_t)-1);
+            node.header.size = htodl(sizeof(node)+extSize);
+            dest->writeData(&node, sizeof(node));
+            dest->writeData(extData, extSize);
+        }
     }
 
     return NO_ERROR;
diff --git a/tools/layoutlib/bridge/.settings/README.txt b/tools/layoutlib/bridge/.settings/README.txt
new file mode 100644
index 0000000..9120b20
--- /dev/null
+++ b/tools/layoutlib/bridge/.settings/README.txt
@@ -0,0 +1,2 @@
+Copy this in eclipse project as a .settings folder at the root.
+This ensure proper compilation compliance and warning/error levels.
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/.settings/org.eclipse.jdt.core.prefs b/tools/layoutlib/bridge/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..5381a0e
--- /dev/null
+++ b/tools/layoutlib/bridge/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,93 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.annotation.nonnull=com.android.annotations.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=com.android.annotations.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nonnullisdefault=disabled
+org.eclipse.jdt.core.compiler.annotation.nullable=com.android.annotations.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecInsufficientInfo=warning
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=error
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/tools/layoutlib/bridge/resources/bars/action_bar.xml b/tools/layoutlib/bridge/resources/bars/action_bar.xml
index 51983f2..7adc5af 100644
--- a/tools/layoutlib/bridge/resources/bars/action_bar.xml
+++ b/tools/layoutlib/bridge/resources/bars/action_bar.xml
@@ -1,9 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
-	<ImageView
-			android:layout_height="wrap_content"
-			android:layout_width="wrap_content"/>
-	<TextView
-			android:layout_width="wrap_content"
-			android:layout_height="wrap_content"/>
+    <include layout="@android:layout/action_bar_home" />
+    <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
 </merge>
diff --git a/tools/layoutlib/bridge/src/android/animation/AnimationThread.java b/tools/layoutlib/bridge/src/android/animation/AnimationThread.java
index af83c61..b46134a 100644
--- a/tools/layoutlib/bridge/src/android/animation/AnimationThread.java
+++ b/tools/layoutlib/bridge/src/android/animation/AnimationThread.java
@@ -23,11 +23,10 @@
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.RenderSessionImpl;
 
-import android.animation.ValueAnimator;
 import android.os.Handler;
 import android.os.Handler_Delegate;
-import android.os.Message;
 import android.os.Handler_Delegate.IHandlerCallback;
+import android.os.Message;
 
 import java.util.PriorityQueue;
 import java.util.Queue;
@@ -57,6 +56,7 @@
             mUptimeMillis = uptimeMillis;
         }
 
+        @Override
         public int compareTo(MessageBundle bundle) {
             if (mUptimeMillis < bundle.mUptimeMillis) {
                 return -1;
@@ -85,6 +85,7 @@
         Bridge.prepareThread();
         try {
             Handler_Delegate.setCallback(new IHandlerCallback() {
+                @Override
                 public void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
                     if (msg.what == ValueAnimator.ANIMATION_START /*||
                             FIXME: The ANIMATION_FRAME message no longer exists.  Instead,
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
index 9a8cf04..65a75b0 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
@@ -105,6 +105,7 @@
             mTileModeY = tileModeY;
         }
 
+        @Override
         public java.awt.PaintContext createContext(
                 java.awt.image.ColorModel      colorModel,
                 java.awt.Rectangle             deviceBounds,
@@ -148,13 +149,16 @@
                 mColorModel = colorModel;
             }
 
+            @Override
             public void dispose() {
             }
 
+            @Override
             public java.awt.image.ColorModel getColorModel() {
                 return mColorModel;
             }
 
+            @Override
             public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
                 java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
                         java.awt.image.BufferedImage.TYPE_INT_ARGB);
@@ -240,6 +244,7 @@
         }
 
 
+        @Override
         public int getTransparency() {
             return java.awt.Paint.TRANSLUCENT;
         }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index f797836..16f1575 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -291,6 +291,7 @@
             Paint paint) {
         draw(thisCanvas.mNativeCanvas, paint.mNativePaint, false /*compositeOnly*/,
                 false /*forceSrcMode*/, new GcSnapshot.Drawable() {
+                    @Override
                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
                         for (int i = 0 ; i < count ; i += 4) {
                             graphics.drawLine((int)pts[i + offset], (int)pts[i + offset + 1],
@@ -619,6 +620,7 @@
         final int h = canvasDelegate.mBitmap.getImage().getHeight();
         draw(nativeCanvas, new GcSnapshot.Drawable() {
 
+            @Override
             public void draw(Graphics2D graphics, Paint_Delegate paint) {
                 // reset its transform just in case
                 graphics.setTransform(new AffineTransform());
@@ -651,6 +653,7 @@
 
         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
                 new GcSnapshot.Drawable() {
+                    @Override
                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
                         graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
                     }
@@ -669,6 +672,7 @@
 
         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
                 new GcSnapshot.Drawable() {
+                    @Override
                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
                         int style = paintDelegate.getStyle();
 
@@ -693,6 +697,7 @@
         if (oval.right > oval.left && oval.bottom > oval.top) {
             draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
                     new GcSnapshot.Drawable() {
+                        @Override
                         public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
                             int style = paintDelegate.getStyle();
 
@@ -728,6 +733,7 @@
         if (oval.right > oval.left && oval.bottom > oval.top) {
             draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
                     new GcSnapshot.Drawable() {
+                        @Override
                         public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
                             int style = paintDelegate.getStyle();
 
@@ -757,6 +763,7 @@
 
         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
                 new GcSnapshot.Drawable() {
+                    @Override
                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
                         int style = paintDelegate.getStyle();
 
@@ -789,6 +796,7 @@
 
         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
                 new GcSnapshot.Drawable() {
+                    @Override
                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
                         Shape shape = pathDelegate.getJavaShape();
                         int style = paintDelegate.getStyle();
@@ -892,6 +900,7 @@
 
         draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, false /*forceSrcMode*/,
                 new GcSnapshot.Drawable() {
+                    @Override
                     public void draw(Graphics2D graphics, Paint_Delegate paint) {
                         if (paint != null && paint.isFilterBitmap()) {
                             graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
@@ -931,6 +940,7 @@
         final AffineTransform mtx = matrixDelegate.getAffineTransform();
 
         canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
+                @Override
                 public void draw(Graphics2D graphics, Paint_Delegate paint) {
                     if (paint != null && paint.isFilterBitmap()) {
                         graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
@@ -970,6 +980,7 @@
             final float startX, final float startY, int flags, int paint) {
         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
                 new GcSnapshot.Drawable() {
+            @Override
             public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
                 // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
                 // Any change to this method should be reflected in Paint.measureText
@@ -1279,6 +1290,7 @@
 
         draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0],
                 new GcSnapshot.Drawable() {
+                    @Override
                     public void draw(Graphics2D graphics, Paint_Delegate paint) {
                         if (paint != null && paint.isFilterBitmap()) {
                             graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
diff --git a/tools/layoutlib/bridge/src/android/graphics/Gradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Gradient_Delegate.java
index 38c092d..7475c22 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Gradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Gradient_Delegate.java
@@ -87,6 +87,7 @@
             mTileMode = tileMode;
         }
 
+        @Override
         public int getTransparency() {
             return java.awt.Paint.TRANSLUCENT;
         }
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
index a2ba758..f117fca 100644
--- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
@@ -132,6 +132,7 @@
             mDSize2 = mDx * mDx + mDy * mDy;
         }
 
+        @Override
         public java.awt.PaintContext createContext(
                 java.awt.image.ColorModel      colorModel,
                 java.awt.Rectangle             deviceBounds,
@@ -176,13 +177,16 @@
                 mColorModel = colorModel;
             }
 
+            @Override
             public void dispose() {
             }
 
+            @Override
             public java.awt.image.ColorModel getColorModel() {
                 return mColorModel;
             }
 
+            @Override
             public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
                 java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
                         java.awt.image.BufferedImage.TYPE_INT_ARGB);
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
index 451edd2..5df2a21 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
@@ -474,7 +474,7 @@
         }
 
         Matrix_Delegate other = sManager.getDelegate(other_matrix);
-        if (d == null) {
+        if (other == null) {
             return false;
         }
 
@@ -570,7 +570,7 @@
         }
 
         Matrix_Delegate other = sManager.getDelegate(other_matrix);
-        if (d == null) {
+        if (other == null) {
             return false;
         }
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
index 5e882ce..be27b54 100644
--- a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
@@ -215,6 +215,7 @@
         Paint_Delegate paint_delegate = Paint_Delegate.getDelegate(paint_instance_or_null);
 
         canvas_delegate.getSnapshot().draw(new GcSnapshot.Drawable() {
+                @Override
                 public void draw(Graphics2D graphics, Paint_Delegate paint) {
                     chunkObject.draw(bitmap_delegate.getImage(), graphics,
                             left, top, right - left, bottom - top, destDensity, srcDensity);
diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
index 9bf78b4..3fe45fa 100644
--- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
@@ -118,6 +118,7 @@
             mRadius = radius;
         }
 
+        @Override
         public java.awt.PaintContext createContext(
                 java.awt.image.ColorModel     colorModel,
                 java.awt.Rectangle            deviceBounds,
@@ -162,13 +163,16 @@
                 mColorModel = colorModel;
             }
 
+            @Override
             public void dispose() {
             }
 
+            @Override
             public java.awt.image.ColorModel getColorModel() {
                 return mColorModel;
             }
 
+            @Override
             public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
                 java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
                         java.awt.image.BufferedImage.TYPE_INT_ARGB);
diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
index 966e06e..13ae12e 100644
--- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
@@ -110,6 +110,7 @@
             mCy = cy;
         }
 
+        @Override
         public java.awt.PaintContext createContext(
                 java.awt.image.ColorModel     colorModel,
                 java.awt.Rectangle            deviceBounds,
@@ -154,13 +155,16 @@
                 mColorModel = colorModel;
             }
 
+            @Override
             public void dispose() {
             }
 
+            @Override
             public java.awt.image.ColorModel getColorModel() {
                 return mColorModel;
             }
 
+            @Override
             public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
                 java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
                         java.awt.image.BufferedImage.TYPE_INT_ARGB);
diff --git a/tools/layoutlib/bridge/src/android/view/SurfaceView.java b/tools/layoutlib/bridge/src/android/view/SurfaceView.java
index ce32da9..6aa4b3b 100644
--- a/tools/layoutlib/bridge/src/android/view/SurfaceView.java
+++ b/tools/layoutlib/bridge/src/android/view/SurfaceView.java
@@ -27,7 +27,7 @@
  * Mock version of the SurfaceView.
  * Only non override public methods from the real SurfaceView have been added in there.
  * Methods that take an unknown class as parameter or as return object, have been removed for now.
- * 
+ *
  * TODO: generate automatically.
  *
  */
@@ -36,7 +36,7 @@
     public SurfaceView(Context context) {
         this(context, null);
     }
-    
+
     public SurfaceView(Context context, AttributeSet attrs) {
         this(context, attrs , 0);
     }
@@ -44,53 +44,66 @@
     public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
     }
-    
+
     public SurfaceHolder getHolder() {
         return mSurfaceHolder;
     }
 
     private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
-        
+
+        @Override
         public boolean isCreating() {
             return false;
         }
 
+        @Override
         public void addCallback(Callback callback) {
         }
 
+        @Override
         public void removeCallback(Callback callback) {
         }
-        
+
+        @Override
         public void setFixedSize(int width, int height) {
         }
 
+        @Override
         public void setSizeFromLayout() {
         }
 
+        @Override
         public void setFormat(int format) {
         }
 
+        @Override
         public void setType(int type) {
         }
 
+        @Override
         public void setKeepScreenOn(boolean screenOn) {
         }
-        
+
+        @Override
         public Canvas lockCanvas() {
             return null;
         }
 
+        @Override
         public Canvas lockCanvas(Rect dirty) {
             return null;
         }
 
+        @Override
         public void unlockCanvasAndPost(Canvas canvas) {
         }
 
+        @Override
         public Surface getSurface() {
             return null;
         }
 
+        @Override
         public Rect getSurfaceFrame() {
             return null;
         }
diff --git a/tools/layoutlib/bridge/src/com/android/internal/textservice/ITextServicesManager_Stub_Delegate.java b/tools/layoutlib/bridge/src/com/android/internal/textservice/ITextServicesManager_Stub_Delegate.java
index 9efdcaf..3017292 100644
--- a/tools/layoutlib/bridge/src/com/android/internal/textservice/ITextServicesManager_Stub_Delegate.java
+++ b/tools/layoutlib/bridge/src/com/android/internal/textservice/ITextServicesManager_Stub_Delegate.java
@@ -43,28 +43,33 @@
 
     private static class FakeTextServicesManager implements ITextServicesManager {
 
+        @Override
         public void finishSpellCheckerService(ISpellCheckerSessionListener arg0)
                 throws RemoteException {
             // TODO Auto-generated method stub
 
         }
 
+        @Override
         public SpellCheckerInfo getCurrentSpellChecker(String arg0) throws RemoteException {
             // TODO Auto-generated method stub
             return null;
         }
 
+        @Override
         public SpellCheckerSubtype getCurrentSpellCheckerSubtype(String arg0, boolean arg1)
                 throws RemoteException {
             // TODO Auto-generated method stub
             return null;
         }
 
+        @Override
         public SpellCheckerInfo[] getEnabledSpellCheckers() throws RemoteException {
             // TODO Auto-generated method stub
             return null;
         }
 
+        @Override
         public void getSpellCheckerService(String arg0, String arg1,
                 ITextServicesSessionListener arg2, ISpellCheckerSessionListener arg3, Bundle arg4)
                 throws RemoteException {
@@ -72,26 +77,31 @@
 
         }
 
+        @Override
         public boolean isSpellCheckerEnabled() throws RemoteException {
             // TODO Auto-generated method stub
             return false;
         }
 
+        @Override
         public void setCurrentSpellChecker(String arg0, String arg1) throws RemoteException {
             // TODO Auto-generated method stub
 
         }
 
+        @Override
         public void setCurrentSpellCheckerSubtype(String arg0, int arg1) throws RemoteException {
             // TODO Auto-generated method stub
 
         }
 
+        @Override
         public void setSpellCheckerEnabled(boolean arg0) throws RemoteException {
             // TODO Auto-generated method stub
 
         }
 
+        @Override
         public IBinder asBinder() {
             // TODO Auto-generated method stub
             return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
index c64ab65..e28866e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
@@ -19,7 +19,7 @@
 import android.content.ContentProviderOperation;
 import android.content.ContentProviderResult;
 import android.content.ContentValues;
-import android.content.ICancelationSignal;
+import android.content.ICancellationSignal;
 import android.content.IContentProvider;
 import android.content.OperationApplicationException;
 import android.content.res.AssetFileDescriptor;
@@ -92,7 +92,7 @@
 
     @Override
     public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3, String arg4,
-            ICancelationSignal arg5) throws RemoteException {
+            ICancellationSignal arg5) throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
@@ -124,7 +124,7 @@
     }
 
     @Override
-    public ICancelationSignal createCancelationSignal() throws RemoteException {
+    public ICancellationSignal createCancellationSignal() throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
index 2a52888..db0694c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
@@ -37,151 +37,179 @@
  */
 public class BridgeIInputMethodManager implements IInputMethodManager {
 
+    @Override
     public void addClient(IInputMethodClient arg0, IInputContext arg1, int arg2, int arg3)
             throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void finishInput(IInputMethodClient arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public InputMethodSubtype getCurrentInputMethodSubtype() throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
 
+    @Override
     public List<InputMethodInfo> getEnabledInputMethodList() throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
 
+    @Override
     public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo arg0,
             boolean arg1) throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
 
+    @Override
     public List<InputMethodInfo> getInputMethodList() throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
 
+    @Override
     public InputMethodSubtype getLastInputMethodSubtype() throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
 
+    @Override
     public List getShortcutInputMethodsAndSubtypes() throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
 
+    @Override
     public void hideMySoftInput(IBinder arg0, int arg1) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public boolean hideSoftInput(IInputMethodClient arg0, int arg1, ResultReceiver arg2)
             throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public boolean notifySuggestionPicked(SuggestionSpan arg0, String arg1, int arg2)
             throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public void registerSuggestionSpansForNotification(SuggestionSpan[] arg0)
             throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void removeClient(IInputMethodClient arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void setAdditionalInputMethodSubtypes(String arg0, InputMethodSubtype[] arg1)
             throws RemoteException {
         // TODO Auto-generated method stub
     }
 
+    @Override
     public boolean setCurrentInputMethodSubtype(InputMethodSubtype arg0) throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public void setImeWindowStatus(IBinder arg0, int arg1, int arg2) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void setInputMethod(IBinder arg0, String arg1) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void setInputMethodAndSubtype(IBinder arg0, String arg1, InputMethodSubtype arg2)
             throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public boolean setInputMethodEnabled(String arg0, boolean arg1) throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public void showInputMethodAndSubtypeEnablerFromClient(IInputMethodClient arg0, String arg1)
             throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void showInputMethodPickerFromClient(IInputMethodClient arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void showMySoftInput(IBinder arg0, int arg1) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public boolean showSoftInput(IInputMethodClient arg0, int arg1, ResultReceiver arg2)
             throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public InputBindResult startInput(IInputMethodClient arg0, IInputContext arg1, EditorInfo arg2,
             boolean arg3, boolean arg4) throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
 
+    @Override
     public boolean switchToLastInputMethod(IBinder arg0) throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public void updateStatusIcon(IBinder arg0, String arg1, int arg2) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void windowGainedFocus(IInputMethodClient arg0, IBinder arg1, boolean arg2,
             boolean arg3, int arg4, boolean arg5, int arg6) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public IBinder asBinder() {
         // TODO Auto-generated method stub
         return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeLayoutParamsMapAttributes.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeLayoutParamsMapAttributes.java
index d208408..f5912e7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeLayoutParamsMapAttributes.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeLayoutParamsMapAttributes.java
@@ -37,6 +37,7 @@
         mAttributes = attributes;
     }
 
+    @Override
     public String getAttributeValue(String namespace, String name) {
         if (BridgeConstants.NS_RESOURCES.equals(namespace)) {
             return mAttributes.get(name);
@@ -49,93 +50,114 @@
     // BridgeContext#obtainStyledAttributes(AttributeSet, int[], int, int)
     // Should they ever be called, we'll just implement them on a need basis.
 
+    @Override
     public int getAttributeCount() {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public String getAttributeName(int index) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public String getAttributeValue(int index) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public String getPositionDescription() {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public int getAttributeNameResource(int index) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public int getAttributeListValue(String namespace, String attribute,
             String[] options, int defaultValue) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public boolean getAttributeBooleanValue(String namespace, String attribute,
             boolean defaultValue) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public int getAttributeResourceValue(String namespace, String attribute,
             int defaultValue) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public int getAttributeIntValue(String namespace, String attribute,
             int defaultValue) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public int getAttributeUnsignedIntValue(String namespace, String attribute,
             int defaultValue) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public float getAttributeFloatValue(String namespace, String attribute,
             float defaultValue) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public int getAttributeListValue(int index,
             String[] options, int defaultValue) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public int getAttributeResourceValue(int index, int defaultValue) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public int getAttributeIntValue(int index, int defaultValue) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public int getAttributeUnsignedIntValue(int index, int defaultValue) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public float getAttributeFloatValue(int index, float defaultValue) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public String getIdAttribute() {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public String getClassAttribute() {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public int getIdAttributeResourceValue(int defaultValue) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public int getStyleAttribute() {
         throw new UnsupportedOperationException();
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index e13380e..79606a4 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -24,72 +24,67 @@
 import android.os.RemoteException;
 import android.view.DragEvent;
 import android.view.IWindow;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
 
 /**
  * Implementation of {@link IWindow} to pass to the AttachInfo.
  */
 public final class BridgeWindow implements IWindow {
 
+    @Override
     public void dispatchAppVisibility(boolean arg0) throws RemoteException {
         // pass for now.
     }
 
+    @Override
     public void dispatchGetNewSurface() throws RemoteException {
         // pass for now.
     }
-
-    public void dispatchKey(KeyEvent arg0) throws RemoteException {
-        // pass for now.
-    }
-
-    public void dispatchPointer(MotionEvent arg0, long arg1, boolean arg2) throws RemoteException {
-        // pass for now.
-    }
-
-    public void dispatchTrackball(MotionEvent arg0, long arg1, boolean arg2)
-    throws RemoteException {
-        // pass for now.
-    }
-
+    @Override
     public void executeCommand(String arg0, String arg1, ParcelFileDescriptor arg2)
             throws RemoteException {
         // pass for now.
     }
 
+    @Override
     public void resized(int arg0, int arg1, Rect arg2, Rect arg3, boolean arg4, Configuration arg5)
             throws RemoteException {
         // pass for now.
     }
 
+    @Override
     public void windowFocusChanged(boolean arg0, boolean arg1) throws RemoteException {
         // pass for now.
     }
 
+    @Override
     public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
             boolean sync) {
         // pass for now.
     }
 
+    @Override
     public void dispatchWallpaperCommand(String action, int x, int y,
             int z, Bundle extras, boolean sync) {
         // pass for now.
     }
 
+    @Override
     public void closeSystemDialogs(String reason) {
         // pass for now.
     }
 
+    @Override
     public void dispatchDragEvent(DragEvent event) {
         // pass for now.
     }
 
+    @Override
     public void dispatchSystemUiVisibilityChanged(int seq, int globalUi,
             int localValue, int localChanges) {
         // pass for now.
     }
 
+    @Override
     public IBinder asBinder() {
         // pass for now.
         return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
index 516725e..bef2c95 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
@@ -67,288 +67,345 @@
 
     // ---- implementation of IWindowManager that we care about ----
 
+    @Override
     public int getRotation() throws RemoteException {
         return mRotation;
     }
 
+    @Override
     public int getMaximumSizeDimension() throws RemoteException {
         return 0;
     }
 
+    @Override
     public void getDisplaySize(Point arg0) throws RemoteException {
     }
 
+    @Override
     public void getRealDisplaySize(Point arg0) throws RemoteException {
     }
 
     // ---- unused implementation of IWindowManager ----
 
+    @Override
     public boolean canStatusBarHide() throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, boolean arg4)
             throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void addWindowToken(IBinder arg0, int arg1) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void clearForcedDisplaySize() throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void closeSystemDialogs(String arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void disableKeyguard(IBinder arg0, String arg1) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void executeAppTransition() throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void exitKeyguardSecurely(IOnKeyguardExitResult arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void freezeRotation(int arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public float getAnimationScale(int arg0) throws RemoteException {
         // TODO Auto-generated method stub
         return 0;
     }
 
+    @Override
     public float[] getAnimationScales() throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
 
+    @Override
     public int getAppOrientation(IApplicationToken arg0) throws RemoteException {
         // TODO Auto-generated method stub
         return 0;
     }
 
+    @Override
     public int getDPadKeycodeState(int arg0) throws RemoteException {
         // TODO Auto-generated method stub
         return 0;
     }
 
+    @Override
     public int getDPadScancodeState(int arg0) throws RemoteException {
         // TODO Auto-generated method stub
         return 0;
     }
 
 
+    @Override
     public InputDevice getInputDevice(int arg0) throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
 
+    @Override
     public int[] getInputDeviceIds() throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
 
+    @Override
     public int getKeycodeState(int arg0) throws RemoteException {
         // TODO Auto-generated method stub
         return 0;
     }
 
+    @Override
     public int getKeycodeStateForDevice(int arg0, int arg1) throws RemoteException {
         // TODO Auto-generated method stub
         return 0;
     }
 
 
+    @Override
     public int getPendingAppTransition() throws RemoteException {
         // TODO Auto-generated method stub
         return 0;
     }
 
 
+    @Override
     public int getScancodeState(int arg0) throws RemoteException {
         // TODO Auto-generated method stub
         return 0;
     }
 
+    @Override
     public int getScancodeStateForDevice(int arg0, int arg1) throws RemoteException {
         // TODO Auto-generated method stub
         return 0;
     }
 
+    @Override
     public int getSwitchState(int arg0) throws RemoteException {
         // TODO Auto-generated method stub
         return 0;
     }
 
+    @Override
     public int getSwitchStateForDevice(int arg0, int arg1) throws RemoteException {
         // TODO Auto-generated method stub
         return 0;
     }
 
+    @Override
     public int getTrackballKeycodeState(int arg0) throws RemoteException {
         // TODO Auto-generated method stub
         return 0;
     }
 
+    @Override
     public int getTrackballScancodeState(int arg0) throws RemoteException {
         // TODO Auto-generated method stub
         return 0;
     }
 
+    @Override
     public boolean hasKeys(int[] arg0, boolean[] arg1) throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public boolean inKeyguardRestrictedInputMode() throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public boolean injectInputEventNoWait(InputEvent arg0) throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public boolean injectKeyEvent(KeyEvent arg0, boolean arg1) throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public boolean injectPointerEvent(MotionEvent arg0, boolean arg1) throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public boolean injectTrackballEvent(MotionEvent arg0, boolean arg1) throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public boolean inputMethodClientHasFocus(IInputMethodClient arg0) throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public boolean isKeyguardLocked() throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public boolean isKeyguardSecure() throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public boolean isViewServerRunning() throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public InputChannel monitorInput(String arg0) throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
 
+    @Override
     public void moveAppToken(int arg0, IBinder arg1) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void moveAppTokensToBottom(List<IBinder> arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void moveAppTokensToTop(List<IBinder> arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public IWindowSession openSession(IInputMethodClient arg0, IInputContext arg1)
             throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
 
+    @Override
     public void overridePendingAppTransition(String arg0, int arg1, int arg2)
             throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void pauseKeyDispatching(IBinder arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void prepareAppTransition(int arg0, boolean arg1) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void reenableKeyguard(IBinder arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void removeAppToken(IBinder arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void removeWindowToken(IBinder arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void resumeKeyDispatching(IBinder arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public Bitmap screenshotApplications(IBinder arg0, int arg1, int arg2) throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
 
+    @Override
     public void setAnimationScale(int arg0, float arg1) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void setAnimationScales(float[] arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void setAppGroupId(IBinder arg0, int arg1) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void setAppOrientation(IApplicationToken arg0, int arg1) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void setAppStartingWindow(IBinder arg0, String arg1, int arg2, CompatibilityInfo arg3,
             CharSequence arg4, int arg5, int arg6, int arg7, IBinder arg8, boolean arg9)
             throws RemoteException {
@@ -356,122 +413,147 @@
 
     }
 
+    @Override
     public void setAppVisibility(IBinder arg0, boolean arg1) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void setAppWillBeHidden(IBinder arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void setEventDispatching(boolean arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void setFocusedApp(IBinder arg0, boolean arg1) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void setForcedDisplaySize(int arg0, int arg1) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void setInTouchMode(boolean arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void setNewConfiguration(Configuration arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void setPointerSpeed(int arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void updateRotation(boolean arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void setStrictModeVisualIndicatorPreference(String arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void showStrictModeViolation(boolean arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void startAppFreezingScreen(IBinder arg0, int arg1) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public boolean startViewServer(int arg0) throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public void statusBarVisibilityChanged(int arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void stopAppFreezingScreen(IBinder arg0, boolean arg1) throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public boolean stopViewServer() throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
 
+    @Override
     public void thawRotation() throws RemoteException {
         // TODO Auto-generated method stub
 
     }
 
+    @Override
     public Configuration updateOrientationFromAppTokens(Configuration arg0, IBinder arg1)
             throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
 
+    @Override
     public int watchRotation(IRotationWatcher arg0) throws RemoteException {
         // TODO Auto-generated method stub
         return 0;
     }
 
+    @Override
     public void waitForWindowDrawn(IBinder token, IRemoteCallback callback) {
         // TODO Auto-generated method stub
     }
-    
+
+    @Override
     public IBinder asBinder() {
         // TODO Auto-generated method stub
         return null;
     }
 
+    @Override
     public int getPreferredOptionsPanelGravity() throws RemoteException {
         return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
     }
 
+    @Override
     public void dismissKeyguard() {
     }
 
+    @Override
     public boolean hasNavigationBar() {
         return false; // should this return something else?
     }
 
+    @Override
     public void lockNow() {
         // TODO Auto-generated method stub
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index a640a91..d3721ed 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -26,7 +26,6 @@
 import android.view.IWindow;
 import android.view.IWindowSession;
 import android.view.InputChannel;
-import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.SurfaceView;
 import android.view.WindowManager.LayoutParams;
@@ -37,6 +36,7 @@
  */
 public final class BridgeWindowSession implements IWindowSession {
 
+    @Override
     public int add(IWindow arg0, int seq, LayoutParams arg1, int arg2, Rect arg3,
             InputChannel outInputchannel)
             throws RemoteException {
@@ -44,40 +44,30 @@
         return 0;
     }
 
+    @Override
     public int addWithoutInputChannel(IWindow arg0, int seq, LayoutParams arg1, int arg2, Rect arg3)
             throws RemoteException {
         // pass for now.
         return 0;
     }
 
+    @Override
     public void finishDrawing(IWindow arg0) throws RemoteException {
         // pass for now.
     }
 
-    public void finishKey(IWindow arg0) throws RemoteException {
-        // pass for now.
-    }
-
+    @Override
     public boolean getInTouchMode() throws RemoteException {
         // pass for now.
         return false;
     }
 
+    @Override
     public boolean performHapticFeedback(IWindow window, int effectId, boolean always) {
         // pass for now.
         return false;
     }
-
-    public MotionEvent getPendingPointerMove(IWindow arg0) throws RemoteException {
-        // pass for now.
-        return null;
-    }
-
-    public MotionEvent getPendingTrackballMove(IWindow arg0) throws RemoteException {
-        // pass for now.
-        return null;
-    }
-
+    @Override
     public int relayout(IWindow arg0, int seq, LayoutParams arg1, int arg2, int arg3, int arg4,
             int arg4_5, Rect arg5, Rect arg6, Rect arg7, Configuration arg7b, Surface arg8)
             throws RemoteException {
@@ -85,35 +75,43 @@
         return 0;
     }
 
+    @Override
     public void performDeferredDestroy(IWindow window) {
         // pass for now.
     }
 
+    @Override
     public boolean outOfMemory(IWindow window) throws RemoteException {
         return false;
     }
 
+    @Override
     public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
         // pass for now.
     }
 
+    @Override
     public void remove(IWindow arg0) throws RemoteException {
         // pass for now.
     }
 
+    @Override
     public void setInTouchMode(boolean arg0) throws RemoteException {
         // pass for now.
     }
 
+    @Override
     public void setTransparentRegion(IWindow arg0, Region arg1) throws RemoteException {
         // pass for now.
     }
 
+    @Override
     public void setInsets(IWindow window, int touchable, Rect contentInsets,
             Rect visibleInsets, Region touchableRegion) {
         // pass for now.
     }
 
+    @Override
     public IBinder prepareDrag(IWindow window, int flags,
             int thumbnailWidth, int thumbnailHeight, Surface outSurface)
             throws RemoteException {
@@ -121,6 +119,7 @@
         return null;
     }
 
+    @Override
     public boolean performDrag(IWindow window, IBinder dragToken,
             float touchX, float touchY, float thumbCenterX, float thumbCenterY,
             ClipData data)
@@ -129,49 +128,47 @@
         return false;
     }
 
+    @Override
     public void reportDropResult(IWindow window, boolean consumed) throws RemoteException {
         // pass for now
     }
 
+    @Override
     public void dragRecipientEntered(IWindow window) throws RemoteException {
         // pass for now
     }
 
+    @Override
     public void dragRecipientExited(IWindow window) throws RemoteException {
         // pass for now
     }
 
+    @Override
     public void setWallpaperPosition(IBinder window, float x, float y,
         float xStep, float yStep) {
         // pass for now.
     }
 
+    @Override
     public void wallpaperOffsetsComplete(IBinder window) {
         // pass for now.
     }
 
+    @Override
     public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
             int z, Bundle extras, boolean sync) {
         // pass for now.
         return null;
     }
 
+    @Override
     public void wallpaperCommandComplete(IBinder window, Bundle result) {
         // pass for now.
     }
 
-    public void closeSystemDialogs(String reason) {
-        // pass for now.
-    }
-
+    @Override
     public IBinder asBinder() {
         // pass for now.
         return null;
     }
-
-    public IBinder prepareDrag(IWindow arg0, boolean arg1, int arg2, int arg3, Surface arg4)
-            throws RemoteException {
-        // TODO Auto-generated method stub
-        return null;
-    }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
index f8ed4f7..ac8712e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
@@ -95,6 +95,7 @@
 
     // ------- XmlResourceParser implementation
 
+    @Override
     public void setFeature(String name, boolean state)
             throws XmlPullParserException {
         if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) {
@@ -106,6 +107,7 @@
         throw new XmlPullParserException("Unsupported feature: " + name);
     }
 
+    @Override
     public boolean getFeature(String name) {
         if (FEATURE_PROCESS_NAMESPACES.equals(name)) {
             return true;
@@ -116,82 +118,101 @@
         return false;
     }
 
+    @Override
     public void setProperty(String name, Object value) throws XmlPullParserException {
         throw new XmlPullParserException("setProperty() not supported");
     }
 
+    @Override
     public Object getProperty(String name) {
         return null;
     }
 
+    @Override
     public void setInput(Reader in) throws XmlPullParserException {
         mParser.setInput(in);
     }
 
+    @Override
     public void setInput(InputStream inputStream, String inputEncoding)
             throws XmlPullParserException {
         mParser.setInput(inputStream, inputEncoding);
     }
 
+    @Override
     public void defineEntityReplacementText(String entityName,
             String replacementText) throws XmlPullParserException {
         throw new XmlPullParserException(
                 "defineEntityReplacementText() not supported");
     }
 
+    @Override
     public String getNamespacePrefix(int pos) throws XmlPullParserException {
         throw new XmlPullParserException("getNamespacePrefix() not supported");
     }
 
+    @Override
     public String getInputEncoding() {
         return null;
     }
 
+    @Override
     public String getNamespace(String prefix) {
         throw new RuntimeException("getNamespace() not supported");
     }
 
+    @Override
     public int getNamespaceCount(int depth) throws XmlPullParserException {
         throw new XmlPullParserException("getNamespaceCount() not supported");
     }
 
+    @Override
     public String getPositionDescription() {
         return "Binary XML file line #" + getLineNumber();
     }
 
+    @Override
     public String getNamespaceUri(int pos) throws XmlPullParserException {
         throw new XmlPullParserException("getNamespaceUri() not supported");
     }
 
+    @Override
     public int getColumnNumber() {
         return -1;
     }
 
+    @Override
     public int getDepth() {
         return mParser.getDepth();
     }
 
+    @Override
     public String getText() {
         return mParser.getText();
     }
 
+    @Override
     public int getLineNumber() {
         return mParser.getLineNumber();
     }
 
+    @Override
     public int getEventType() {
         return mEventType;
     }
 
+    @Override
     public boolean isWhitespace() throws XmlPullParserException {
         // Original comment: whitespace was stripped by aapt.
         return mParser.isWhitespace();
     }
 
+    @Override
     public String getPrefix() {
         throw new RuntimeException("getPrefix not supported");
     }
 
+    @Override
     public char[] getTextCharacters(int[] holderForStartAndLength) {
         String txt = getText();
         char[] chars = null;
@@ -204,55 +225,68 @@
         return chars;
     }
 
+    @Override
     public String getNamespace() {
         return mParser.getNamespace();
     }
 
+    @Override
     public String getName() {
         return mParser.getName();
     }
 
+    @Override
     public String getAttributeNamespace(int index) {
         return mParser.getAttributeNamespace(index);
     }
 
+    @Override
     public String getAttributeName(int index) {
         return mParser.getAttributeName(index);
     }
 
+    @Override
     public String getAttributePrefix(int index) {
         throw new RuntimeException("getAttributePrefix not supported");
     }
 
+    @Override
     public boolean isEmptyElementTag() {
         // XXX Need to detect this.
         return false;
     }
 
+    @Override
     public int getAttributeCount() {
         return mParser.getAttributeCount();
     }
 
+    @Override
     public String getAttributeValue(int index) {
         return mParser.getAttributeValue(index);
     }
 
+    @Override
     public String getAttributeType(int index) {
         return "CDATA";
     }
 
+    @Override
     public boolean isAttributeDefault(int index) {
         return false;
     }
 
+    @Override
     public int nextToken() throws XmlPullParserException, IOException {
         return next();
     }
 
+    @Override
     public String getAttributeValue(String namespace, String name) {
         return mParser.getAttributeValue(namespace, name);
     }
 
+    @Override
     public int next() throws XmlPullParserException, IOException {
         if (!mStarted) {
             mStarted = true;
@@ -313,6 +347,7 @@
         return "????";
     }
 
+    @Override
     public void require(int type, String namespace, String name)
             throws XmlPullParserException {
         if (type != getEventType()
@@ -322,6 +357,7 @@
                     + getPositionDescription());
     }
 
+    @Override
     public String nextText() throws XmlPullParserException, IOException {
         if (getEventType() != START_TAG) {
             throw new XmlPullParserException(getPositionDescription()
@@ -348,6 +384,7 @@
         }
     }
 
+    @Override
     public int nextTag() throws XmlPullParserException, IOException {
         int eventType = next();
         if (eventType == TEXT && isWhitespace()) { // skip whitespace
@@ -363,76 +400,94 @@
     // AttributeSet implementation
 
 
+    @Override
     public void close() {
         // pass
     }
 
+    @Override
     public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
         return mAttrib.getAttributeBooleanValue(index, defaultValue);
     }
 
+    @Override
     public boolean getAttributeBooleanValue(String namespace, String attribute,
             boolean defaultValue) {
         return mAttrib.getAttributeBooleanValue(namespace, attribute, defaultValue);
     }
 
+    @Override
     public float getAttributeFloatValue(int index, float defaultValue) {
         return mAttrib.getAttributeFloatValue(index, defaultValue);
     }
 
+    @Override
     public float getAttributeFloatValue(String namespace, String attribute, float defaultValue) {
         return mAttrib.getAttributeFloatValue(namespace, attribute, defaultValue);
     }
 
+    @Override
     public int getAttributeIntValue(int index, int defaultValue) {
         return mAttrib.getAttributeIntValue(index, defaultValue);
     }
 
+    @Override
     public int getAttributeIntValue(String namespace, String attribute, int defaultValue) {
         return mAttrib.getAttributeIntValue(namespace, attribute, defaultValue);
     }
 
+    @Override
     public int getAttributeListValue(int index, String[] options, int defaultValue) {
         return mAttrib.getAttributeListValue(index, options, defaultValue);
     }
 
+    @Override
     public int getAttributeListValue(String namespace, String attribute,
             String[] options, int defaultValue) {
         return mAttrib.getAttributeListValue(namespace, attribute, options, defaultValue);
     }
 
+    @Override
     public int getAttributeNameResource(int index) {
         return mAttrib.getAttributeNameResource(index);
     }
 
+    @Override
     public int getAttributeResourceValue(int index, int defaultValue) {
         return mAttrib.getAttributeResourceValue(index, defaultValue);
     }
 
+    @Override
     public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
         return mAttrib.getAttributeResourceValue(namespace, attribute, defaultValue);
     }
 
+    @Override
     public int getAttributeUnsignedIntValue(int index, int defaultValue) {
         return mAttrib.getAttributeUnsignedIntValue(index, defaultValue);
     }
 
+    @Override
     public int getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue) {
         return mAttrib.getAttributeUnsignedIntValue(namespace, attribute, defaultValue);
     }
 
+    @Override
     public String getClassAttribute() {
         return mAttrib.getClassAttribute();
     }
 
+    @Override
     public String getIdAttribute() {
         return mAttrib.getIdAttribute();
     }
 
+    @Override
     public int getIdAttributeResourceValue(int defaultValue) {
         return mAttrib.getIdAttributeResourceValue(defaultValue);
     }
 
+    @Override
     public int getStyleAttribute() {
         return mAttrib.getStyleAttribute();
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index 72ed351..1817ab5 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -132,7 +132,7 @@
                 if (bitmap != null) {
                     BitmapDrawable drawable = new BitmapDrawable(getContext().getResources(),
                             bitmap);
-                    imageView.setBackgroundDrawable(drawable);
+                    imageView.setImageDrawable(drawable);
                 }
             }
         }
@@ -145,6 +145,14 @@
         }
     }
 
+    protected void loadIconById(int id, String iconReference) {
+        ResourceValue value = getResourceValue(iconReference);
+        if (value != null) {
+            loadIconById(id, value);
+        }
+    }
+
+
     protected Drawable loadIcon(int index, ResourceType type, String name) {
         BridgeContext bridgeContext = (BridgeContext) mContext;
         RenderResources res = bridgeContext.getRenderResources();
@@ -162,34 +170,64 @@
         if (child instanceof ImageView) {
             ImageView imageView = (ImageView) child;
 
-            Drawable drawable = ResourceHelper.getDrawable(
-                    value, (BridgeContext) mContext);
-            if (drawable != null) {
-                imageView.setBackgroundDrawable(drawable);
-            }
-
-            return drawable;
+            return loadIcon(imageView, value);
         }
 
         return null;
     }
 
+    private Drawable loadIconById(int id, ResourceValue value) {
+        View child = findViewById(id);
+        if (child instanceof ImageView) {
+            ImageView imageView = (ImageView) child;
+
+            return loadIcon(imageView, value);
+        }
+
+        return null;
+    }
+
+
+    private Drawable loadIcon(ImageView imageView, ResourceValue value) {
+        Drawable drawable = ResourceHelper.getDrawable(value, (BridgeContext) mContext);
+        if (drawable != null) {
+            imageView.setImageDrawable(drawable);
+        }
+
+        return drawable;
+    }
+
     protected TextView setText(int index, String stringReference) {
         View child = getChildAt(index);
         if (child instanceof TextView) {
             TextView textView = (TextView) child;
-            ResourceValue value = getResourceValue(stringReference);
-            if (value != null) {
-                textView.setText(value.getValue());
-            } else {
-                textView.setText(stringReference);
-            }
+            setText(textView, stringReference);
             return textView;
         }
 
         return null;
     }
 
+    protected TextView setTextById(int id, String stringReference) {
+        View child = findViewById(id);
+        if (child instanceof TextView) {
+            TextView textView = (TextView) child;
+            setText(textView, stringReference);
+            return textView;
+        }
+
+        return null;
+    }
+
+    private void setText(TextView textView, String stringReference) {
+        ResourceValue value = getResourceValue(stringReference);
+        if (value != null) {
+            textView.setText(value.getValue());
+        } else {
+            textView.setText(stringReference);
+        }
+    }
+
     protected void setStyle(String themeEntryName) {
 
         BridgeContext bridgeContext = (BridgeContext) mContext;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java
index f6edea4..68f5aba 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java
@@ -34,7 +34,7 @@
         // Cannot access the inside items through id because no R.id values have been
         // created for them.
         // We do know the order though.
-        loadIcon(0, icon);
+        loadIconById(android.R.id.home, icon);
         mTextView = setText(1, label);
 
         setStyle("actionBarStyle");
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index d5400d7..6840f46 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -33,10 +33,10 @@
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.Result;
-import com.android.ide.common.rendering.api.SessionParams;
-import com.android.ide.common.rendering.api.ViewInfo;
 import com.android.ide.common.rendering.api.Result.Status;
+import com.android.ide.common.rendering.api.SessionParams;
 import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
+import com.android.ide.common.rendering.api.ViewInfo;
 import com.android.internal.util.XmlUtils;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
@@ -69,8 +69,8 @@
 import android.view.AttachInfo_Accessor;
 import android.view.BridgeInflater;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewGroup.MarginLayoutParams;
 import android.widget.AbsListView;
@@ -82,8 +82,8 @@
 import android.widget.ListView;
 import android.widget.QuickContactBadge;
 import android.widget.TabHost;
-import android.widget.TabWidget;
 import android.widget.TabHost.TabSpec;
+import android.widget.TabWidget;
 
 import java.awt.AlphaComposite;
 import java.awt.Color;
@@ -835,6 +835,7 @@
                 previousTransition.addTransitionListener(new TransitionListener() {
                     private int mChangeDisappearingCount = 0;
 
+                    @Override
                     public void startTransition(LayoutTransition transition, ViewGroup container,
                             View view, int transitionType) {
                         if (transitionType == LayoutTransition.CHANGE_DISAPPEARING) {
@@ -842,6 +843,7 @@
                         }
                     }
 
+                    @Override
                     public void endTransition(LayoutTransition transition, ViewGroup container,
                             View view, int transitionType) {
                         if (transitionType == LayoutTransition.CHANGE_DISAPPEARING) {
@@ -1227,6 +1229,7 @@
             TabSpec spec = tabHost.newTabSpec("tag").setIndicator("Tab Label",
                     tabHost.getResources().getDrawable(android.R.drawable.ic_menu_info_details))
                     .setContent(new TabHost.TabContentFactory() {
+                        @Override
                         public View createTabContent(String tag) {
                             return new LinearLayout(getContext());
                         }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java
index c9bb424..22570b9 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java
@@ -74,38 +74,46 @@
         }
     }
 
+    @Override
     public boolean isEnabled(int position) {
         return true;
     }
 
+    @Override
     public int getCount() {
         return mItems.size();
     }
 
+    @Override
     public Object getItem(int position) {
         return mItems.get(position);
     }
 
+    @Override
     public long getItemId(int position) {
         return position;
     }
 
+    @Override
     public int getItemViewType(int position) {
         return mItems.get(position).getType();
     }
 
+    @Override
     public View getView(int position, View convertView, ViewGroup parent) {
         // we don't care about recycling here because we never scroll.
         AdapterItem item = mItems.get(position);
         return getView(item, null /*parentGroup*/, convertView, parent);
     }
 
+    @Override
     public int getViewTypeCount() {
         return mTypes.size();
     }
 
     // ---- SpinnerAdapter
 
+    @Override
     public View getDropDownView(int position, View convertView, ViewGroup parent) {
         // pass
         return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java
index 2c492e3..199e040 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java
@@ -99,23 +99,28 @@
 
     // ---- ExpandableListAdapter
 
+    @Override
     public int getGroupCount() {
         return mItems.size();
     }
 
+    @Override
     public int getChildrenCount(int groupPosition) {
         AdapterItem item = mItems.get(groupPosition);
         return item.getChildren().size();
     }
 
+    @Override
     public Object getGroup(int groupPosition) {
         return mItems.get(groupPosition);
     }
 
+    @Override
     public Object getChild(int groupPosition, int childPosition) {
         return getChildItem(groupPosition, childPosition);
     }
 
+    @Override
     public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
             ViewGroup parent) {
         // we don't care about recycling here because we never scroll.
@@ -123,6 +128,7 @@
         return getView(item, null /*parentItem*/, convertView, parent);
     }
 
+    @Override
     public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
             View convertView, ViewGroup parent) {
         // we don't care about recycling here because we never scroll.
@@ -131,48 +137,59 @@
         return getView(item, parentItem, convertView, parent);
     }
 
+    @Override
     public long getGroupId(int groupPosition) {
         return groupPosition;
     }
 
+    @Override
     public long getChildId(int groupPosition, int childPosition) {
         return childPosition;
     }
 
+    @Override
     public long getCombinedGroupId(long groupId) {
         return groupId << 16 | 0x0000FFFF;
     }
 
+    @Override
     public long getCombinedChildId(long groupId, long childId) {
         return groupId << 16 | childId;
     }
 
+    @Override
     public boolean isChildSelectable(int groupPosition, int childPosition) {
         return true;
     }
 
+    @Override
     public void onGroupCollapsed(int groupPosition) {
         // pass
     }
 
+    @Override
     public void onGroupExpanded(int groupPosition) {
         // pass
     }
 
     // ---- HeterogeneousExpandableList
 
+    @Override
     public int getChildType(int groupPosition, int childPosition) {
         return getChildItem(groupPosition, childPosition).getType();
     }
 
+    @Override
     public int getChildTypeCount() {
         return mChildrenTypes.size();
     }
 
+    @Override
     public int getGroupType(int groupPosition) {
         return mItems.get(groupPosition).getType();
     }
 
+    @Override
     public int getGroupTypeCount() {
         return mGroupTypes.size();
     }
diff --git a/tools/layoutlib/create/.settings/README.txt b/tools/layoutlib/create/.settings/README.txt
new file mode 100644
index 0000000..9120b20
--- /dev/null
+++ b/tools/layoutlib/create/.settings/README.txt
@@ -0,0 +1,2 @@
+Copy this in eclipse project as a .settings folder at the root.
+This ensure proper compilation compliance and warning/error levels.
\ No newline at end of file
diff --git a/tools/layoutlib/create/.settings/org.eclipse.jdt.core.prefs b/tools/layoutlib/create/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..5381a0e
--- /dev/null
+++ b/tools/layoutlib/create/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,93 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.annotation.nonnull=com.android.annotations.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=com.android.annotations.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nonnullisdefault=disabled
+org.eclipse.jdt.core.compiler.annotation.nullable=com.android.annotations.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecInsufficientInfo=warning
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=error
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 70c8a00..4b33474 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -27,6 +27,7 @@
      * Returns the list of class from layoutlib_create to inject in layoutlib.
      * The list can be empty but must not be null.
      */
+    @Override
     public Class<?>[] getInjectedClasses() {
         return INJECTED_CLASSES;
     }
@@ -35,6 +36,7 @@
      * Returns the list of methods to rewrite as delegates.
      * The list can be empty but must not be null.
      */
+    @Override
     public String[] getDelegateMethods() {
         return DELEGATE_METHODS;
     }
@@ -43,6 +45,7 @@
      * Returns the list of classes on which to delegate all native methods.
      * The list can be empty but must not be null.
      */
+    @Override
     public String[] getDelegateClassNatives() {
         return DELEGATE_CLASS_NATIVES;
     }
@@ -54,6 +57,7 @@
      * <p/>
      * This usage is deprecated. Please use method 'delegates' instead.
      */
+    @Override
     public String[] getOverriddenMethods() {
         return OVERRIDDEN_METHODS;
     }
@@ -63,6 +67,7 @@
      * of class to replace followed by the new FQCN.
      * The list can be empty but must not be null.
      */
+    @Override
     public String[] getRenamedClasses() {
         return RENAMED_CLASSES;
     }
@@ -74,6 +79,7 @@
      * the methods to delete.
      * The list can be empty but must not be null.
      */
+    @Override
     public String[] getDeleteReturns() {
         return DELETE_RETURNS;
     }
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java
index 627ea17..7d1e4cf 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodAdapter.java
@@ -28,13 +28,14 @@
      * A stub method is being invoked.
      * <p/>
      * Known limitation: caller arguments are not available.
-     *  
+     *
      * @param signature The signature of the method being invoked, composed of the
      *                  binary class name followed by the method descriptor (aka argument
      *                  types). Example: "com/foo/MyClass/InnerClass/printInt(I)V".
      * @param isNative True if the method was a native method.
      * @param caller The calling object. Null for static methods, "this" for instance methods.
      */
+    @Override
     public void onInvokeV(String signature, boolean isNative, Object caller) {
     }
 
@@ -43,6 +44,7 @@
      * @see #onInvokeV(String, boolean, Object)
      * @return an integer, or a boolean, or a short or a byte.
      */
+    @Override
     public int onInvokeI(String signature, boolean isNative, Object caller) {
         onInvokeV(signature, isNative, caller);
         return 0;
@@ -53,6 +55,7 @@
      * @see #onInvokeV(String, boolean, Object)
      * @return a long.
      */
+    @Override
     public long onInvokeL(String signature, boolean isNative, Object caller) {
         onInvokeV(signature, isNative, caller);
         return 0;
@@ -63,6 +66,7 @@
      * @see #onInvokeV(String, boolean, Object)
      * @return a float.
      */
+    @Override
     public float onInvokeF(String signature, boolean isNative, Object caller) {
         onInvokeV(signature, isNative, caller);
         return 0;
@@ -73,6 +77,7 @@
      * @see #onInvokeV(String, boolean, Object)
      * @return a double.
      */
+    @Override
     public double onInvokeD(String signature, boolean isNative, Object caller) {
         onInvokeV(signature, isNative, caller);
         return 0;
@@ -83,6 +88,7 @@
      * @see #onInvokeV(String, boolean, Object)
      * @return an object.
      */
+    @Override
     public Object onInvokeA(String signature, boolean isNative, Object caller) {
         onInvokeV(signature, isNative, caller);
         return null;
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
index f4ff389..7b76a5b 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -65,24 +65,29 @@
     public void testClassRenaming() throws IOException, LogAbortException {
 
         ICreateInfo ci = new ICreateInfo() {
+            @Override
             public Class<?>[] getInjectedClasses() {
                 // classes to inject in the final JAR
                 return new Class<?>[0];
             }
 
+            @Override
             public String[] getDelegateMethods() {
                 return new String[0];
             }
 
+            @Override
             public String[] getDelegateClassNatives() {
                 return new String[0];
             }
 
+            @Override
             public String[] getOverriddenMethods() {
                 // methods to force override
                 return new String[0];
             }
 
+            @Override
             public String[] getRenamedClasses() {
                 // classes to rename (so that we can replace them)
                 return new String[] {
@@ -91,6 +96,7 @@
                 };
             }
 
+            @Override
             public String[] getDeleteReturns() {
                  // methods deleted from their return type.
                 return new String[0];