Merge "Add missing try/finallys"
diff --git a/api/current.txt b/api/current.txt
index 1055c24..ef6aed7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3175,6 +3175,7 @@
     method public void finishActivity(int);
     method public void finishActivityFromChild(android.app.Activity, int);
     method public void finishAffinity();
+    method public void finishAndRemoveTask();
     method public void finishFromChild(android.app.Activity);
     method public void finishWithTransition();
     method public android.app.ActionBar getActionBar();
@@ -16813,11 +16814,24 @@
 
 package android.nfc.cardemulation {
 
+  public final class AidGroup implements android.os.Parcelable {
+    ctor public AidGroup(java.util.ArrayList<java.lang.String>, java.lang.String);
+    method public int describeContents();
+    method public java.util.ArrayList<java.lang.String> getAids();
+    method public java.lang.String getCategory();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int MAX_NUM_AIDS = 256; // 0x100
+  }
+
   public final class CardEmulation {
+    method public android.nfc.cardemulation.AidGroup getAidGroupForService(android.content.ComponentName, java.lang.String);
     method public static synchronized android.nfc.cardemulation.CardEmulation getInstance(android.nfc.NfcAdapter);
     method public int getSelectionModeForCategory(java.lang.String);
     method public boolean isDefaultServiceForAid(android.content.ComponentName, java.lang.String);
     method public boolean isDefaultServiceForCategory(android.content.ComponentName, java.lang.String);
+    method public boolean registerAidGroupForService(android.content.ComponentName, android.nfc.cardemulation.AidGroup);
+    method public boolean removeAidGroupForService(android.content.ComponentName, java.lang.String);
     field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
     field public static final java.lang.String CATEGORY_OTHER = "other";
     field public static final java.lang.String CATEGORY_PAYMENT = "payment";
@@ -19256,11 +19270,12 @@
     field public static final int L = 10000; // 0x2710
   }
 
-  public final class Bundle implements java.lang.Cloneable android.os.Parcelable {
+  public final class Bundle extends android.os.CommonBundle {
     ctor public Bundle();
     ctor public Bundle(java.lang.ClassLoader);
     ctor public Bundle(int);
     ctor public Bundle(android.os.Bundle);
+    ctor public Bundle(android.os.PersistableBundle);
     method public void clear();
     method public java.lang.Object clone();
     method public boolean containsKey(java.lang.String);
@@ -19298,6 +19313,7 @@
     method public T getParcelable(java.lang.String);
     method public android.os.Parcelable[] getParcelableArray(java.lang.String);
     method public java.util.ArrayList<T> getParcelableArrayList(java.lang.String);
+    method public android.os.PersistableBundle getPersistableBundle(java.lang.String);
     method public java.io.Serializable getSerializable(java.lang.String);
     method public short getShort(java.lang.String);
     method public short getShort(java.lang.String, short);
@@ -19311,6 +19327,7 @@
     method public boolean isEmpty();
     method public java.util.Set<java.lang.String> keySet();
     method public void putAll(android.os.Bundle);
+    method public void putAll(android.os.PersistableBundle);
     method public void putBinder(java.lang.String, android.os.IBinder);
     method public void putBoolean(java.lang.String, boolean);
     method public void putBooleanArray(java.lang.String, boolean[]);
@@ -19334,6 +19351,7 @@
     method public void putParcelable(java.lang.String, android.os.Parcelable);
     method public void putParcelableArray(java.lang.String, android.os.Parcelable[]);
     method public void putParcelableArrayList(java.lang.String, java.util.ArrayList<? extends android.os.Parcelable>);
+    method public void putPersistableBundle(java.lang.String, android.os.PersistableBundle);
     method public void putSerializable(java.lang.String, java.io.Serializable);
     method public void putShort(java.lang.String, short);
     method public void putShortArray(java.lang.String, short[]);
@@ -19362,6 +19380,9 @@
     method public abstract void onCancel();
   }
 
+   abstract class CommonBundle implements java.lang.Cloneable android.os.Parcelable {
+  }
+
   public class ConditionVariable {
     ctor public ConditionVariable();
     ctor public ConditionVariable(boolean);
@@ -19788,6 +19809,8 @@
     method public final void readMap(java.util.Map, java.lang.ClassLoader);
     method public final T readParcelable(java.lang.ClassLoader);
     method public final android.os.Parcelable[] readParcelableArray(java.lang.ClassLoader);
+    method public final android.os.PersistableBundle readPersistableBundle();
+    method public final android.os.PersistableBundle readPersistableBundle(java.lang.ClassLoader);
     method public final java.io.Serializable readSerializable();
     method public final android.util.SparseArray readSparseArray(java.lang.ClassLoader);
     method public final android.util.SparseBooleanArray readSparseBooleanArray();
@@ -19828,6 +19851,7 @@
     method public final void writeNoException();
     method public final void writeParcelable(android.os.Parcelable, int);
     method public final void writeParcelableArray(T[], int);
+    method public final void writePersistableBundle(android.os.PersistableBundle);
     method public final void writeSerializable(java.io.Serializable);
     method public final void writeSparseArray(android.util.SparseArray<java.lang.Object>);
     method public final void writeSparseBooleanArray(android.util.SparseBooleanArray);
@@ -19938,6 +19962,51 @@
     field public static final int PATTERN_SIMPLE_GLOB = 2; // 0x2
   }
 
+  public final class PersistableBundle extends android.os.CommonBundle {
+    ctor public PersistableBundle();
+    ctor public PersistableBundle(java.lang.ClassLoader);
+    ctor public PersistableBundle(int);
+    ctor public PersistableBundle(android.os.PersistableBundle);
+    method public void clear();
+    method public java.lang.Object clone();
+    method public boolean containsKey(java.lang.String);
+    method public int describeContents();
+    method public java.lang.Object get(java.lang.String);
+    method public java.lang.ClassLoader getClassLoader();
+    method public double getDouble(java.lang.String);
+    method public double getDouble(java.lang.String, double);
+    method public double[] getDoubleArray(java.lang.String);
+    method public int getInt(java.lang.String);
+    method public int getInt(java.lang.String, int);
+    method public int[] getIntArray(java.lang.String);
+    method public long getLong(java.lang.String);
+    method public long getLong(java.lang.String, long);
+    method public long[] getLongArray(java.lang.String);
+    method public android.os.PersistableBundle getPersistableBundle(java.lang.String);
+    method public java.lang.String getString(java.lang.String);
+    method public java.lang.String getString(java.lang.String, java.lang.String);
+    method public java.lang.String[] getStringArray(java.lang.String);
+    method public boolean isEmpty();
+    method public java.util.Set<java.lang.String> keySet();
+    method public void putAll(android.os.PersistableBundle);
+    method public void putDouble(java.lang.String, double);
+    method public void putDoubleArray(java.lang.String, double[]);
+    method public void putInt(java.lang.String, int);
+    method public void putIntArray(java.lang.String, int[]);
+    method public void putLong(java.lang.String, long);
+    method public void putLongArray(java.lang.String, long[]);
+    method public void putPersistableBundle(java.lang.String, android.os.PersistableBundle);
+    method public void putString(java.lang.String, java.lang.String);
+    method public void putStringArray(java.lang.String, java.lang.String[]);
+    method public void readFromParcel(android.os.Parcel);
+    method public void remove(java.lang.String);
+    method public void setClassLoader(java.lang.ClassLoader);
+    method public int size();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final android.os.PersistableBundle EMPTY;
+  }
+
   public final class PowerManager {
     method public void goToSleep(long);
     method public boolean isInteractive();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a5a06e3..599a608 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4314,11 +4314,10 @@
     }
 
     /**
-     * Call this when your activity is done and should be closed.  The
-     * ActivityResult is propagated back to whoever launched you via
-     * onActivityResult().
+     * Finishes the current activity and specifies whether to remove the task associated with this
+     * activity.
      */
-    public void finish() {
+    private void finish(boolean finishTask) {
         if (mParent == null) {
             int resultCode;
             Intent resultData;
@@ -4332,7 +4331,7 @@
                     resultData.prepareToLeaveProcess();
                 }
                 if (ActivityManagerNative.getDefault()
-                    .finishActivity(mToken, resultCode, resultData)) {
+                        .finishActivity(mToken, resultCode, resultData, finishTask)) {
                     mFinished = true;
                 }
             } catch (RemoteException e) {
@@ -4344,6 +4343,15 @@
     }
 
     /**
+     * Call this when your activity is done and should be closed.  The
+     * ActivityResult is propagated back to whoever launched you via
+     * onActivityResult().
+     */
+    public void finish() {
+        finish(false);
+    }
+
+    /**
      * Finish this activity as well as all activities immediately below it
      * in the current task that have the same affinity.  This is typically
      * used when an application can be launched on to another task (such as
@@ -4442,6 +4450,14 @@
     }
 
     /**
+     * Call this when your activity is done and should be closed and the task should be completely
+     * removed as a part of finishing the Activity.
+     */
+    public void finishAndRemoveTask() {
+        finish(true);
+    }
+
+    /**
      * Called when an activity you launched exits, giving you the requestCode
      * you started it with, the resultCode it returned, and any additional
      * data from it.  The <var>resultCode</var> will be
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 44c74d8..10831f2 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -263,7 +263,8 @@
             if (data.readInt() != 0) {
                 resultData = Intent.CREATOR.createFromParcel(data);
             }
-            boolean res = finishActivity(token, resultCode, resultData);
+            boolean finishTask = (data.readInt() != 0);
+            boolean res = finishActivity(token, resultCode, resultData, finishTask);
             reply.writeNoException();
             reply.writeInt(res ? 1 : 0);
             return true;
@@ -2342,7 +2343,7 @@
         data.recycle();
         return result != 0;
     }
-    public boolean finishActivity(IBinder token, int resultCode, Intent resultData)
+    public boolean finishActivity(IBinder token, int resultCode, Intent resultData, boolean finishTask)
             throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -2355,6 +2356,7 @@
         } else {
             data.writeInt(0);
         }
+        data.writeInt(finishTask ? 1 : 0);
         mRemote.transact(FINISH_ACTIVITY_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 965f815..88eae7f 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2363,7 +2363,7 @@
             // manager to stop us.
             try {
                 ActivityManagerNative.getDefault()
-                    .finishActivity(r.token, Activity.RESULT_CANCELED, null);
+                    .finishActivity(r.token, Activity.RESULT_CANCELED, null, false);
             } catch (RemoteException ex) {
                 // Ignore
             }
@@ -2984,7 +2984,7 @@
             // just end this activity.
             try {
                 ActivityManagerNative.getDefault()
-                    .finishActivity(token, Activity.RESULT_CANCELED, null);
+                    .finishActivity(token, Activity.RESULT_CANCELED, null, false);
             } catch (RemoteException ex) {
             }
         }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index bfbd339..52003f1 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -79,7 +79,7 @@
             int flagsMask, int flagsValues, Bundle options) throws RemoteException;
     public boolean startNextMatchingActivity(IBinder callingActivity,
             Intent intent, Bundle options) throws RemoteException;
-    public boolean finishActivity(IBinder token, int code, Intent data)
+    public boolean finishActivity(IBinder token, int code, Intent data, boolean finishTask)
             throws RemoteException;
     public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException;
     public boolean finishActivityAffinity(IBinder token) throws RemoteException;
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index a6a04d1..5cf61a8 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -41,8 +41,6 @@
     @Deprecated
     public static final int DISABLE_NOTIFICATION_TICKER
             = View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER;
-    public static final int DISABLE_PRIVATE_NOTIFICATIONS
-            = View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER;
     public static final int DISABLE_SYSTEM_INFO = View.STATUS_BAR_DISABLE_SYSTEM_INFO;
     public static final int DISABLE_HOME = View.STATUS_BAR_DISABLE_HOME;
     public static final int DISABLE_RECENT = View.STATUS_BAR_DISABLE_RECENT;
diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl
index b8a5ba7..ae9796b 100644
--- a/core/java/android/nfc/INfcCardEmulation.aidl
+++ b/core/java/android/nfc/INfcCardEmulation.aidl
@@ -17,6 +17,7 @@
 package android.nfc;
 
 import android.content.ComponentName;
+import android.nfc.cardemulation.AidGroup;
 import android.nfc.cardemulation.ApduServiceInfo;
 import android.os.RemoteCallback;
 
@@ -29,5 +30,8 @@
     boolean isDefaultServiceForAid(int userHandle, in ComponentName service, String aid);
     boolean setDefaultServiceForCategory(int userHandle, in ComponentName service, String category);
     boolean setDefaultForNextTap(int userHandle, in ComponentName service);
+    boolean registerAidGroupForService(int userHandle, in ComponentName service, in AidGroup aidGroup);
+    AidGroup getAidGroupForService(int userHandle, in ComponentName service, String category);
+    boolean removeAidGroupForService(int userHandle, in ComponentName service, String category);
     List<ApduServiceInfo> getServices(int userHandle, in String category);
 }
diff --git a/core/java/android/nfc/cardemulation/AidGroup.aidl b/core/java/android/nfc/cardemulation/AidGroup.aidl
new file mode 100644
index 0000000..56d6fa5
--- /dev/null
+++ b/core/java/android/nfc/cardemulation/AidGroup.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013 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.nfc.cardemulation;
+
+parcelable AidGroup;
diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java
new file mode 100644
index 0000000..2820f40
--- /dev/null
+++ b/core/java/android/nfc/cardemulation/AidGroup.java
@@ -0,0 +1,165 @@
+package android.nfc.cardemulation;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * The AidGroup class represents a group of ISO/IEC 7816-4
+ * Application Identifiers (AIDs) for a specific application
+ * category, along with a description resource describing
+ * the group.
+ */
+public final class AidGroup implements Parcelable {
+    /**
+     * The maximum number of AIDs that can be present in any one group.
+     */
+    public static final int MAX_NUM_AIDS = 256;
+
+    static final String TAG = "AidGroup";
+
+    final ArrayList<String> aids;
+    final String category;
+    final String description;
+
+    /**
+     * Creates a new AidGroup object.
+     *
+     * @param aids The list of AIDs present in the group
+     * @param category The category of this group
+     */
+    public AidGroup(ArrayList<String> aids, String category) {
+        if (aids == null || aids.size() == 0) {
+            throw new IllegalArgumentException("No AIDS in AID group.");
+        }
+        if (aids.size() > MAX_NUM_AIDS) {
+            throw new IllegalArgumentException("Too many AIDs in AID group.");
+        }
+        if (!isValidCategory(category)) {
+            throw new IllegalArgumentException("Category specified is not valid.");
+        }
+        this.aids = aids;
+        this.category = category;
+        this.description = null;
+    }
+
+    AidGroup(String category, String description) {
+        this.aids = new ArrayList<String>();
+        this.category = category;
+        this.description = description;
+    }
+
+    /**
+     * @return the category of this AID group
+     */
+    public String getCategory() {
+        return category;
+    }
+
+    /**
+     * @return the list of  AIDs in this group
+     */
+    public ArrayList<String> getAids() {
+        return aids;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder out = new StringBuilder("Category: " + category +
+                  ", AIDs:");
+        for (String aid : aids) {
+            out.append(aid);
+            out.append(", ");
+        }
+        return out.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(category);
+        dest.writeInt(aids.size());
+        if (aids.size() > 0) {
+            dest.writeStringList(aids);
+        }
+    }
+
+    public static final Parcelable.Creator<AidGroup> CREATOR =
+            new Parcelable.Creator<AidGroup>() {
+
+        @Override
+        public AidGroup createFromParcel(Parcel source) {
+            String category = source.readString();
+            int listSize = source.readInt();
+            ArrayList<String> aidList = new ArrayList<String>();
+            if (listSize > 0) {
+                source.readStringList(aidList);
+            }
+            return new AidGroup(aidList, category);
+        }
+
+        @Override
+        public AidGroup[] newArray(int size) {
+            return new AidGroup[size];
+        }
+    };
+
+    /**
+     * @hide
+     * Note: description is not serialized, since it's not localized
+     * and resource identifiers don't make sense to persist.
+     */
+    static public AidGroup createFromXml(XmlPullParser parser) throws XmlPullParserException, IOException {
+        String category = parser.getAttributeValue(null, "category");
+        ArrayList<String> aids = new ArrayList<String>();
+        int eventType = parser.getEventType();
+        int minDepth = parser.getDepth();
+        while (eventType != XmlPullParser.END_DOCUMENT && parser.getDepth() >= minDepth) {
+            if (eventType == XmlPullParser.START_TAG) {
+                String tagName = parser.getName();
+                if (tagName.equals("aid")) {
+                    String aid = parser.getAttributeValue(null, "value");
+                    if (aid != null) {
+                        aids.add(aid);
+                    }
+                } else {
+                    Log.d(TAG, "Ignorning unexpected tag: " + tagName);
+                }
+            }
+            eventType = parser.next();
+        }
+        if (category != null && aids.size() > 0) {
+            return new AidGroup(aids, category);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public void writeAsXml(XmlSerializer out) throws IOException {
+        out.attribute(null, "category", category);
+        for (String aid : aids) {
+            out.startTag(null, "aid");
+            out.attribute(null, "value", aid);
+            out.endTag(null, "aid");
+        }
+    }
+
+    boolean isValidCategory(String category) {
+        return CardEmulation.CATEGORY_PAYMENT.equals(category) ||
+                CardEmulation.CATEGORY_OTHER.equals(category);
+    }
+}
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index d7ef4bc..94f35ed 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -35,9 +35,12 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.FileDescriptor;
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Map;
 
 /**
  * @hide
@@ -56,24 +59,19 @@
     final String mDescription;
 
     /**
-     * Convenience AID list
-     */
-    final ArrayList<String> mAids;
-
-    /**
      * Whether this service represents AIDs running on the host CPU
      */
     final boolean mOnHost;
 
     /**
-     * All AID groups this service handles
+     * Mapping from category to static AID group
      */
-    final ArrayList<AidGroup> mAidGroups;
+    final HashMap<String, AidGroup> mStaticAidGroups;
 
     /**
-     * Convenience hashmap
+     * Mapping from category to dynamic AID group
      */
-    final HashMap<String, AidGroup> mCategoryToGroup;
+    final HashMap<String, AidGroup> mDynamicAidGroups;
 
     /**
      * Whether this service should only be started when the device is unlocked.
@@ -86,26 +84,33 @@
     final int mBannerResourceId;
 
     /**
+     * The uid of the package the service belongs to
+     */
+    final int mUid;
+    /**
      * @hide
      */
     public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
-            ArrayList<AidGroup> aidGroups, boolean requiresUnlock, int bannerResource) {
+            ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
+            boolean requiresUnlock, int bannerResource, int uid) {
         this.mService = info;
         this.mDescription = description;
-        this.mAidGroups = aidGroups;
-        this.mAids = new ArrayList<String>();
-        this.mCategoryToGroup = new HashMap<String, AidGroup>();
+        this.mStaticAidGroups = new HashMap<String, AidGroup>();
+        this.mDynamicAidGroups = new HashMap<String, AidGroup>();
         this.mOnHost = onHost;
         this.mRequiresDeviceUnlock = requiresUnlock;
-        for (AidGroup aidGroup : aidGroups) {
-            this.mCategoryToGroup.put(aidGroup.category, aidGroup);
-            this.mAids.addAll(aidGroup.aids);
+        for (AidGroup aidGroup : staticAidGroups) {
+            this.mStaticAidGroups.put(aidGroup.category, aidGroup);
+        }
+        for (AidGroup aidGroup : dynamicAidGroups) {
+            this.mDynamicAidGroups.put(aidGroup.category, aidGroup);
         }
         this.mBannerResourceId = bannerResource;
+        this.mUid = uid;
     }
 
-    public ApduServiceInfo(PackageManager pm, ResolveInfo info, boolean onHost)
-            throws XmlPullParserException, IOException {
+    public ApduServiceInfo(PackageManager pm, ResolveInfo info, boolean onHost) throws
+            XmlPullParserException, IOException {
         ServiceInfo si = info.serviceInfo;
         XmlResourceParser parser = null;
         try {
@@ -163,10 +168,10 @@
                 sa.recycle();
             }
 
-            mAidGroups = new ArrayList<AidGroup>();
-            mCategoryToGroup = new HashMap<String, AidGroup>();
-            mAids = new ArrayList<String>();
+            mStaticAidGroups = new HashMap<String, AidGroup>();
+            mDynamicAidGroups = new HashMap<String, AidGroup>();
             mOnHost = onHost;
+
             final int depth = parser.getDepth();
             AidGroup currentGroup = null;
 
@@ -179,14 +184,14 @@
                     final TypedArray groupAttrs = res.obtainAttributes(attrs,
                             com.android.internal.R.styleable.AidGroup);
                     // Get category of AID group
-                    String groupDescription = groupAttrs.getString(
-                            com.android.internal.R.styleable.AidGroup_description);
                     String groupCategory = groupAttrs.getString(
                             com.android.internal.R.styleable.AidGroup_category);
+                    String groupDescription = groupAttrs.getString(
+                            com.android.internal.R.styleable.AidGroup_description);
                     if (!CardEmulation.CATEGORY_PAYMENT.equals(groupCategory)) {
                         groupCategory = CardEmulation.CATEGORY_OTHER;
                     }
-                    currentGroup = mCategoryToGroup.get(groupCategory);
+                    currentGroup = mStaticAidGroups.get(groupCategory);
                     if (currentGroup != null) {
                         if (!CardEmulation.CATEGORY_OTHER.equals(groupCategory)) {
                             Log.e(TAG, "Not allowing multiple aid-groups in the " +
@@ -200,9 +205,8 @@
                 } else if (eventType == XmlPullParser.END_TAG && "aid-group".equals(tagName) &&
                         currentGroup != null) {
                     if (currentGroup.aids.size() > 0) {
-                        if (!mCategoryToGroup.containsKey(currentGroup.category)) {
-                            mAidGroups.add(currentGroup);
-                            mCategoryToGroup.put(currentGroup.category, currentGroup);
+                        if (!mStaticAidGroups.containsKey(currentGroup.category)) {
+                            mStaticAidGroups.put(currentGroup.category, currentGroup);
                         }
                     } else {
                         Log.e(TAG, "Not adding <aid-group> with empty or invalid AIDs");
@@ -216,7 +220,6 @@
                             toUpperCase();
                     if (isValidAid(aid) && !currentGroup.aids.contains(aid)) {
                         currentGroup.aids.add(aid);
-                        mAids.add(aid);
                     } else {
                         Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid);
                     }
@@ -228,6 +231,8 @@
         } finally {
             if (parser != null) parser.close();
         }
+        // Set uid
+        mUid = si.applicationInfo.uid;
     }
 
     public ComponentName getComponent() {
@@ -235,16 +240,58 @@
                 mService.serviceInfo.name);
     }
 
+    /**
+     * Returns a consolidated list of AIDs from the AID groups
+     * registered by this service. Note that if a service has both
+     * a static (manifest-based) AID group for a category and a dynamic
+     * AID group, only the dynamically registered AIDs will be returned
+     * for that category.
+     * @return List of AIDs registered by the service
+     */
     public ArrayList<String> getAids() {
-        return mAids;
+        final ArrayList<String> aids = new ArrayList<String>();
+        for (AidGroup group : getAidGroups()) {
+            aids.addAll(group.aids);
+        }
+        return aids;
     }
 
+    /**
+     * Returns the registered AID group for this category.
+     */
+    public AidGroup getDynamicAidGroupForCategory(String category) {
+        return mDynamicAidGroups.get(category);
+    }
+
+    public boolean removeDynamicAidGroupForCategory(String category) {
+        return (mDynamicAidGroups.remove(category) != null);
+    }
+
+    /**
+     * Returns a consolidated list of AID groups
+     * registered by this service. Note that if a service has both
+     * a static (manifest-based) AID group for a category and a dynamic
+     * AID group, only the dynamically registered AID group will be returned
+     * for that category.
+     * @return List of AIDs registered by the service
+     */
     public ArrayList<AidGroup> getAidGroups() {
-        return mAidGroups;
+        final ArrayList<AidGroup> groups = new ArrayList<AidGroup>();
+        for (Map.Entry<String, AidGroup> entry : mDynamicAidGroups.entrySet()) {
+            groups.add(entry.getValue());
+        }
+        for (Map.Entry<String, AidGroup> entry : mStaticAidGroups.entrySet()) {
+            if (!mDynamicAidGroups.containsKey(entry.getKey())) {
+                // Consolidate AID groups - don't return static ones
+                // if a dynamic group exists for the category.
+                groups.add(entry.getValue());
+            }
+        }
+        return groups;
     }
 
     public boolean hasCategory(String category) {
-        return mCategoryToGroup.containsKey(category);
+        return (mStaticAidGroups.containsKey(category) || mDynamicAidGroups.containsKey(category));
     }
 
     public boolean isOnHost() {
@@ -259,6 +306,14 @@
         return mDescription;
     }
 
+    public int getUid() {
+        return mUid;
+    }
+
+    public void setOrReplaceDynamicAidGroup(AidGroup aidGroup) {
+        mDynamicAidGroups.put(aidGroup.getCategory(), aidGroup);
+    }
+
     public CharSequence loadLabel(PackageManager pm) {
         return mService.loadLabel(pm);
     }
@@ -304,8 +359,12 @@
         StringBuilder out = new StringBuilder("ApduService: ");
         out.append(getComponent());
         out.append(", description: " + mDescription);
-        out.append(", AID Groups: ");
-        for (AidGroup aidGroup : mAidGroups) {
+        out.append(", Static AID Groups: ");
+        for (AidGroup aidGroup : mStaticAidGroups.values()) {
+            out.append(aidGroup.toString());
+        }
+        out.append(", Dynamic AID Groups: ");
+        for (AidGroup aidGroup : mDynamicAidGroups.values()) {
             out.append(aidGroup.toString());
         }
         return out.toString();
@@ -336,12 +395,17 @@
         mService.writeToParcel(dest, flags);
         dest.writeString(mDescription);
         dest.writeInt(mOnHost ? 1 : 0);
-        dest.writeInt(mAidGroups.size());
-        if (mAidGroups.size() > 0) {
-            dest.writeTypedList(mAidGroups);
+        dest.writeInt(mStaticAidGroups.size());
+        if (mStaticAidGroups.size() > 0) {
+            dest.writeTypedList(new ArrayList<AidGroup>(mStaticAidGroups.values()));
+        }
+        dest.writeInt(mDynamicAidGroups.size());
+        if (mDynamicAidGroups.size() > 0) {
+            dest.writeTypedList(new ArrayList<AidGroup>(mDynamicAidGroups.values()));
         }
         dest.writeInt(mRequiresDeviceUnlock ? 1 : 0);
         dest.writeInt(mBannerResourceId);
+        dest.writeInt(mUid);
     };
 
     public static final Parcelable.Creator<ApduServiceInfo> CREATOR =
@@ -351,14 +415,21 @@
             ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source);
             String description = source.readString();
             boolean onHost = (source.readInt() != 0) ? true : false;
-            ArrayList<AidGroup> aidGroups = new ArrayList<AidGroup>();
-            int numGroups = source.readInt();
-            if (numGroups > 0) {
-                source.readTypedList(aidGroups, AidGroup.CREATOR);
+            ArrayList<AidGroup> staticAidGroups = new ArrayList<AidGroup>();
+            int numStaticGroups = source.readInt();
+            if (numStaticGroups > 0) {
+                source.readTypedList(staticAidGroups, AidGroup.CREATOR);
+            }
+            ArrayList<AidGroup> dynamicAidGroups = new ArrayList<AidGroup>();
+            int numDynamicGroups = source.readInt();
+            if (numDynamicGroups > 0) {
+                source.readTypedList(dynamicAidGroups, AidGroup.CREATOR);
             }
             boolean requiresUnlock = (source.readInt() != 0) ? true : false;
             int bannerResource = source.readInt();
-            return new ApduServiceInfo(info, onHost, description, aidGroups, requiresUnlock, bannerResource);
+            int uid = source.readInt();
+            return new ApduServiceInfo(info, onHost, description, staticAidGroups,
+                    dynamicAidGroups, requiresUnlock, bannerResource, uid);
         }
 
         @Override
@@ -367,76 +438,22 @@
         }
     };
 
-    public static class AidGroup implements Parcelable {
-        final ArrayList<String> aids;
-        final String category;
-        final String description;
-
-        AidGroup(ArrayList<String> aids, String category, String description) {
-            this.aids = aids;
-            this.category = category;
-            this.description = description;
-        }
-
-        AidGroup(String category, String description) {
-            this.aids = new ArrayList<String>();
-            this.category = category;
-            this.description = description;
-        }
-
-        public String getCategory() {
-            return category;
-        }
-
-        public ArrayList<String> getAids() {
-            return aids;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder out = new StringBuilder("Category: " + category +
-                      ", description: " + description + ", AIDs:");
-            for (String aid : aids) {
-                out.append(aid);
-                out.append(", ");
-            }
-            return out.toString();
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeString(category);
-            dest.writeString(description);
-            dest.writeInt(aids.size());
-            if (aids.size() > 0) {
-                dest.writeStringList(aids);
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("    " + getComponent() +
+                " (Description: " + getDescription() + ")");
+        pw.println("    Static AID groups:");
+        for (AidGroup group : mStaticAidGroups.values()) {
+            pw.println("        Category: " + group.category);
+            for (String aid : group.aids) {
+                pw.println("            AID: " + aid);
             }
         }
-
-        public static final Parcelable.Creator<ApduServiceInfo.AidGroup> CREATOR =
-                new Parcelable.Creator<ApduServiceInfo.AidGroup>() {
-
-            @Override
-            public AidGroup createFromParcel(Parcel source) {
-                String category = source.readString();
-                String description = source.readString();
-                int listSize = source.readInt();
-                ArrayList<String> aidList = new ArrayList<String>();
-                if (listSize > 0) {
-                    source.readStringList(aidList);
-                }
-                return new AidGroup(aidList, category, description);
+        pw.println("    Dynamic AID groups:");
+        for (AidGroup group : mDynamicAidGroups.values()) {
+            pw.println("        Category: " + group.category);
+            for (String aid : group.aids) {
+                pw.println("            AID: " + aid);
             }
-
-            @Override
-            public AidGroup[] newArray(int size) {
-                return new AidGroup[size];
-            }
-        };
+        }
     }
 }
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 58d9616..41f039c 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -168,6 +168,10 @@
         if (manager == null) {
             // Get card emu service
             INfcCardEmulation service = adapter.getCardEmulationService();
+            if (service == null) {
+                Log.e(TAG, "This device does not implement the INfcCardEmulation interface.");
+                throw new UnsupportedOperationException();
+            }
             manager = new CardEmulation(context, service);
             sCardEmus.put(context, manager);
         }
@@ -271,6 +275,109 @@
     }
 
     /**
+     * Registers a group of AIDs for the specified service.
+     *
+     * <p>If an AID group for that category was previously
+     * registered for this service (either statically
+     * through the manifest, or dynamically by using this API),
+     * that AID group will be replaced with this one.
+     *
+     * <p>Note that you can only register AIDs for a service that
+     * is running under the same UID as you are. Typically
+     * this means you need to call this from the same
+     * package as the service itself, though UIDs can also
+     * be shared between packages using shared UIDs.
+     *
+     * @param service The component name of the service
+     * @param aidGroup The group of AIDs to be registered
+     * @return whether the registration was successful.
+     */
+    public boolean registerAidGroupForService(ComponentName service, AidGroup aidGroup) {
+        try {
+            return sService.registerAidGroupForService(UserHandle.myUserId(), service, aidGroup);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.registerAidGroupForService(UserHandle.myUserId(), service,
+                        aidGroup);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Retrieves the currently registered AID group for the specified
+     * category for a service.
+     *
+     * <p>Note that this will only return AID groups that were dynamically
+     * registered using {@link #registerAidGroupForService(ComponentName, AidGroup)}
+     * method. It will *not* return AID groups that were statically registered
+     * in the manifest.
+     *
+     * @param service The component name of the service
+     * @param category The category of the AID group to be returned, e.g. {@link #CATEGORY_PAYMENT}
+     * @return The AID group, or null if it couldn't be found
+     */
+    public AidGroup getAidGroupForService(ComponentName service, String category) {
+        try {
+            return sService.getAidGroupForService(UserHandle.myUserId(), service, category);
+        } catch (RemoteException e) {
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return null;
+            }
+            try {
+                return sService.getAidGroupForService(UserHandle.myUserId(), service, category);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Removes a registered AID group for the specified category for the
+     * service provided.
+     *
+     * <p>Note that this will only remove AID groups that were dynamically
+     * registered using the {@link #registerAidGroupForService(ComponentName, AidGroup)}
+     * method. It will *not* remove AID groups that were statically registered in
+     * the manifest. If a dynamically registered AID group is removed using
+     * this method, and a statically registered AID group for the same category
+     * exists in the manifest, that AID group will become active again.
+     *
+     * @param service The component name of the service
+     * @param category The category of the AID group to be removed, e.g. {@link #CATEGORY_PAYMENT}
+     * @return whether the group was successfully removed.
+     */
+    public boolean removeAidGroupForService(ComponentName service, String category) {
+        try {
+            return sService.removeAidGroupForService(UserHandle.myUserId(), service, category);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.removeAidGroupForService(UserHandle.myUserId(), service, category);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
      * @hide
      */
     public boolean setDefaultServiceForCategory(ComponentName service, String category) {
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index af57507..c85e418 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -17,7 +17,6 @@
 package android.os;
 
 import android.util.ArrayMap;
-import android.util.Log;
 import android.util.SparseArray;
 
 import java.io.Serializable;
@@ -29,47 +28,25 @@
  * A mapping from String values to various Parcelable types.
  *
  */
-public final class Bundle implements Parcelable, Cloneable {
-    private static final String TAG = "Bundle";
-    static final boolean DEBUG = false;
+public final class Bundle extends CommonBundle {
     public static final Bundle EMPTY;
-
-    static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
     static final Parcel EMPTY_PARCEL;
 
     static {
         EMPTY = new Bundle();
         EMPTY.mMap = ArrayMap.EMPTY;
-        EMPTY_PARCEL = Parcel.obtain();
+        EMPTY_PARCEL = CommonBundle.EMPTY_PARCEL;
     }
 
-    // Invariant - exactly one of mMap / mParcelledData will be null
-    // (except inside a call to unparcel)
-
-    /* package */ ArrayMap<String, Object> mMap = null;
-
-    /*
-     * If mParcelledData is non-null, then mMap will be null and the
-     * data are stored as a Parcel containing a Bundle.  When the data
-     * are unparcelled, mParcelledData willbe set to null.
-     */
-    /* package */ Parcel mParcelledData = null;
-
     private boolean mHasFds = false;
     private boolean mFdsKnown = true;
     private boolean mAllowFds = true;
 
     /**
-     * The ClassLoader used when unparcelling data from mParcelledData.
-     */
-    private ClassLoader mClassLoader;
-
-    /**
      * Constructs a new, empty Bundle.
      */
     public Bundle() {
-        mMap = new ArrayMap<String, Object>();
-        mClassLoader = getClass().getClassLoader();
+        super();
     }
 
     /**
@@ -79,11 +56,17 @@
      * @param parcelledData a Parcel containing a Bundle
      */
     Bundle(Parcel parcelledData) {
-        readFromParcel(parcelledData);
+        super(parcelledData);
+
+        mHasFds = mParcelledData.hasFileDescriptors();
+        mFdsKnown = true;
     }
 
     /* package */ Bundle(Parcel parcelledData, int length) {
-        readFromParcelInner(parcelledData, length);
+        super(parcelledData, length);
+
+        mHasFds = mParcelledData.hasFileDescriptors();
+        mFdsKnown = true;
     }
 
     /**
@@ -94,8 +77,7 @@
      * inside of the Bundle.
      */
     public Bundle(ClassLoader loader) {
-        mMap = new ArrayMap<String, Object>();
-        mClassLoader = loader;
+        super(loader);
     }
 
     /**
@@ -105,8 +87,7 @@
      * @param capacity the initial capacity of the Bundle
      */
     public Bundle(int capacity) {
-        mMap = new ArrayMap<String, Object>(capacity);
-        mClassLoader = getClass().getClassLoader();
+        super(capacity);
     }
 
     /**
@@ -116,27 +97,20 @@
      * @param b a Bundle to be copied.
      */
     public Bundle(Bundle b) {
-        if (b.mParcelledData != null) {
-            if (b.mParcelledData == EMPTY_PARCEL) {
-                mParcelledData = EMPTY_PARCEL;
-            } else {
-                mParcelledData = Parcel.obtain();
-                mParcelledData.appendFrom(b.mParcelledData, 0, b.mParcelledData.dataSize());
-                mParcelledData.setDataPosition(0);
-            }
-        } else {
-            mParcelledData = null;
-        }
-
-        if (b.mMap != null) {
-            mMap = new ArrayMap<String, Object>(b.mMap);
-        } else {
-            mMap = null;
-        }
+        super(b);
 
         mHasFds = b.mHasFds;
         mFdsKnown = b.mFdsKnown;
-        mClassLoader = b.mClassLoader;
+    }
+
+    /**
+     * Constructs a Bundle containing a copy of the mappings from the given
+     * PersistableBundle.
+     *
+     * @param b a Bundle to be copied.
+     */
+    public Bundle(PersistableBundle b) {
+        super(b);
     }
 
     /**
@@ -145,37 +119,17 @@
      * @hide
      */
     public static Bundle forPair(String key, String value) {
-        // TODO: optimize this case.
         Bundle b = new Bundle(1);
         b.putString(key, value);
         return b;
     }
 
     /**
-     * TODO: optimize this later (getting just the value part of a Bundle
-     * with a single pair) once Bundle.forPair() above is implemented
-     * with a special single-value Map implementation/serialization.
-     *
-     * Note: value in single-pair Bundle may be null.
-     *
      * @hide
      */
+    @Override
     public String getPairValue() {
-        unparcel();
-        int size = mMap.size();
-        if (size > 1) {
-            Log.w(TAG, "getPairValue() used on Bundle with multiple pairs.");
-        }
-        if (size == 0) {
-            return null;
-        }
-        Object o = mMap.valueAt(0);
-        try {
-            return (String) o;
-        } catch (ClassCastException e) {
-            typeWarning("getPairValue()", o, "String", e);
-            return null;
-        }
+        return super.getPairValue();
     }
 
     /**
@@ -184,15 +138,17 @@
      * @param loader An explicit ClassLoader to use when instantiating objects
      * inside of the Bundle.
      */
+    @Override
     public void setClassLoader(ClassLoader loader) {
-        mClassLoader = loader;
+        super.setClassLoader(loader);
     }
 
     /**
      * Return the ClassLoader currently associated with this Bundle.
      */
+    @Override
     public ClassLoader getClassLoader() {
-        return mClassLoader;
+        return super.getClassLoader();
     }
 
     /** @hide */
@@ -212,52 +168,11 @@
     }
 
     /**
-     * If the underlying data are stored as a Parcel, unparcel them
-     * using the currently assigned class loader.
-     */
-    /* package */ synchronized void unparcel() {
-        if (mParcelledData == null) {
-            if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
-                    + ": no parcelled data");
-            return;
-        }
-
-        if (mParcelledData == EMPTY_PARCEL) {
-            if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
-                    + ": empty");
-            if (mMap == null) {
-                mMap = new ArrayMap<String, Object>(1);
-            } else {
-                mMap.erase();
-            }
-            mParcelledData = null;
-            return;
-        }
-
-        int N = mParcelledData.readInt();
-        if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
-                + ": reading " + N + " maps");
-        if (N < 0) {
-            return;
-        }
-        if (mMap == null) {
-            mMap = new ArrayMap<String, Object>(N);
-        } else {
-            mMap.erase();
-            mMap.ensureCapacity(N);
-        }
-        mParcelledData.readArrayMapInternal(mMap, N, mClassLoader);
-        mParcelledData.recycle();
-        mParcelledData = null;
-        if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
-                + " final map: " + mMap);
-    }
-
-    /**
      * @hide
      */
+    @Override
     public boolean isParcelled() {
-        return mParcelledData != null;
+        return super.isParcelled();
     }
 
     /**
@@ -265,25 +180,26 @@
      *
      * @return the number of mappings as an int.
      */
+    @Override
     public int size() {
-        unparcel();
-        return mMap.size();
+        return super.size();
     }
 
     /**
      * Returns true if the mapping of this Bundle is empty, false otherwise.
      */
+    @Override
     public boolean isEmpty() {
-        unparcel();
-        return mMap.isEmpty();
+        return super.isEmpty();
     }
 
     /**
      * Removes all elements from the mapping of this Bundle.
      */
+    @Override
     public void clear() {
-        unparcel();
-        mMap.clear();
+        super.clear();
+
         mHasFds = false;
         mFdsKnown = true;
     }
@@ -295,9 +211,9 @@
      * @param key a String key
      * @return true if the key is part of the mapping, false otherwise
      */
+    @Override
     public boolean containsKey(String key) {
-        unparcel();
-        return mMap.containsKey(key);
+        return super.containsKey(key);
     }
 
     /**
@@ -306,9 +222,9 @@
      * @param key a String key
      * @return an Object, or null
      */
+    @Override
     public Object get(String key) {
-        unparcel();
-        return mMap.get(key);
+        return super.get(key);
     }
 
     /**
@@ -316,24 +232,33 @@
      *
      * @param key a String key
      */
+    @Override
     public void remove(String key) {
-        unparcel();
-        mMap.remove(key);
+        super.remove(key);
     }
 
     /**
      * Inserts all mappings from the given Bundle into this Bundle.
      *
-     * @param map a Bundle
+     * @param bundle a Bundle
      */
-    public void putAll(Bundle map) {
+    public void putAll(Bundle bundle) {
         unparcel();
-        map.unparcel();
-        mMap.putAll(map.mMap);
+        bundle.unparcel();
+        mMap.putAll(bundle.mMap);
 
         // fd state is now known if and only if both bundles already knew
-        mHasFds |= map.mHasFds;
-        mFdsKnown = mFdsKnown && map.mFdsKnown;
+        mHasFds |= bundle.mHasFds;
+        mFdsKnown = mFdsKnown && bundle.mFdsKnown;
+    }
+
+    /**
+     * Inserts all mappings from the given PersistableBundle into this Bundle.
+     *
+     * @param bundle a PersistableBundle
+     */
+    public void putAll(PersistableBundle bundle) {
+        super.putAll(bundle);
     }
 
     /**
@@ -341,9 +266,9 @@
      *
      * @return a Set of String keys
      */
+    @Override
     public Set<String> keySet() {
-        unparcel();
-        return mMap.keySet();
+        return super.keySet();
     }
 
     /**
@@ -352,7 +277,7 @@
     public boolean hasFileDescriptors() {
         if (!mFdsKnown) {
             boolean fdFound = false;    // keep going until we find one or run out of data
-            
+
             if (mParcelledData != null) {
                 if (mParcelledData.hasFileDescriptors()) {
                     fdFound = true;
@@ -390,8 +315,7 @@
                         ArrayList array = (ArrayList) obj;
                         // an ArrayList here might contain either Strings or
                         // Parcelables; only look inside for Parcelables
-                        if ((array.size() > 0)
-                                && (array.get(0) instanceof Parcelable)) {
+                        if (!array.isEmpty() && (array.get(0) instanceof Parcelable)) {
                             for (int n = array.size() - 1; n >= 0; n--) {
                                 Parcelable p = (Parcelable) array.get(n);
                                 if (p != null && ((p.describeContents()
@@ -410,7 +334,7 @@
         }
         return mHasFds;
     }
-    
+
     /**
      * Inserts a Boolean value into the mapping of this Bundle, replacing
      * any existing value for the given key.  Either key or value may be null.
@@ -418,9 +342,9 @@
      * @param key a String, or null
      * @param value a Boolean, or null
      */
+    @Override
     public void putBoolean(String key, boolean value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putBoolean(key, value);
     }
 
     /**
@@ -430,9 +354,9 @@
      * @param key a String, or null
      * @param value a byte
      */
+    @Override
     public void putByte(String key, byte value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putByte(key, value);
     }
 
     /**
@@ -442,9 +366,9 @@
      * @param key a String, or null
      * @param value a char, or null
      */
+    @Override
     public void putChar(String key, char value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putChar(key, value);
     }
 
     /**
@@ -454,9 +378,9 @@
      * @param key a String, or null
      * @param value a short
      */
+    @Override
     public void putShort(String key, short value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putShort(key, value);
     }
 
     /**
@@ -466,9 +390,9 @@
      * @param key a String, or null
      * @param value an int, or null
      */
+    @Override
     public void putInt(String key, int value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putInt(key, value);
     }
 
     /**
@@ -478,9 +402,9 @@
      * @param key a String, or null
      * @param value a long
      */
+    @Override
     public void putLong(String key, long value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putLong(key, value);
     }
 
     /**
@@ -490,9 +414,9 @@
      * @param key a String, or null
      * @param value a float
      */
+    @Override
     public void putFloat(String key, float value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putFloat(key, value);
     }
 
     /**
@@ -502,9 +426,9 @@
      * @param key a String, or null
      * @param value a double
      */
+    @Override
     public void putDouble(String key, double value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putDouble(key, value);
     }
 
     /**
@@ -514,9 +438,9 @@
      * @param key a String, or null
      * @param value a String, or null
      */
+    @Override
     public void putString(String key, String value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putString(key, value);
     }
 
     /**
@@ -526,9 +450,9 @@
      * @param key a String, or null
      * @param value a CharSequence, or null
      */
+    @Override
     public void putCharSequence(String key, CharSequence value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putCharSequence(key, value);
     }
 
     /**
@@ -567,7 +491,7 @@
      * @param value an ArrayList of Parcelable objects, or null
      */
     public void putParcelableArrayList(String key,
-        ArrayList<? extends Parcelable> value) {
+            ArrayList<? extends Parcelable> value) {
         unparcel();
         mMap.put(key, value);
         mFdsKnown = false;
@@ -602,9 +526,9 @@
      * @param key a String, or null
      * @param value an ArrayList<Integer> object, or null
      */
+    @Override
     public void putIntegerArrayList(String key, ArrayList<Integer> value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putIntegerArrayList(key, value);
     }
 
     /**
@@ -614,9 +538,9 @@
      * @param key a String, or null
      * @param value an ArrayList<String> object, or null
      */
+    @Override
     public void putStringArrayList(String key, ArrayList<String> value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putStringArrayList(key, value);
     }
 
     /**
@@ -626,9 +550,9 @@
      * @param key a String, or null
      * @param value an ArrayList<CharSequence> object, or null
      */
+    @Override
     public void putCharSequenceArrayList(String key, ArrayList<CharSequence> value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putCharSequenceArrayList(key, value);
     }
 
     /**
@@ -638,9 +562,9 @@
      * @param key a String, or null
      * @param value a Serializable object, or null
      */
+    @Override
     public void putSerializable(String key, Serializable value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putSerializable(key, value);
     }
 
     /**
@@ -650,9 +574,9 @@
      * @param key a String, or null
      * @param value a boolean array object, or null
      */
+    @Override
     public void putBooleanArray(String key, boolean[] value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putBooleanArray(key, value);
     }
 
     /**
@@ -662,9 +586,9 @@
      * @param key a String, or null
      * @param value a byte array object, or null
      */
+    @Override
     public void putByteArray(String key, byte[] value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putByteArray(key, value);
     }
 
     /**
@@ -674,9 +598,9 @@
      * @param key a String, or null
      * @param value a short array object, or null
      */
+    @Override
     public void putShortArray(String key, short[] value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putShortArray(key, value);
     }
 
     /**
@@ -686,9 +610,9 @@
      * @param key a String, or null
      * @param value a char array object, or null
      */
+    @Override
     public void putCharArray(String key, char[] value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putCharArray(key, value);
     }
 
     /**
@@ -698,9 +622,9 @@
      * @param key a String, or null
      * @param value an int array object, or null
      */
+    @Override
     public void putIntArray(String key, int[] value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putIntArray(key, value);
     }
 
     /**
@@ -710,9 +634,9 @@
      * @param key a String, or null
      * @param value a long array object, or null
      */
+    @Override
     public void putLongArray(String key, long[] value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putLongArray(key, value);
     }
 
     /**
@@ -722,9 +646,9 @@
      * @param key a String, or null
      * @param value a float array object, or null
      */
+    @Override
     public void putFloatArray(String key, float[] value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putFloatArray(key, value);
     }
 
     /**
@@ -734,9 +658,9 @@
      * @param key a String, or null
      * @param value a double array object, or null
      */
+    @Override
     public void putDoubleArray(String key, double[] value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putDoubleArray(key, value);
     }
 
     /**
@@ -746,9 +670,9 @@
      * @param key a String, or null
      * @param value a String array object, or null
      */
+    @Override
     public void putStringArray(String key, String[] value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putStringArray(key, value);
     }
 
     /**
@@ -758,9 +682,9 @@
      * @param key a String, or null
      * @param value a CharSequence array object, or null
      */
+    @Override
     public void putCharSequenceArray(String key, CharSequence[] value) {
-        unparcel();
-        mMap.put(key, value);
+        super.putCharSequenceArray(key, value);
     }
 
     /**
@@ -776,6 +700,17 @@
     }
 
     /**
+     * Inserts a PersistableBundle value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a Bundle object, or null
+     */
+    public void putPersistableBundle(String key, PersistableBundle value) {
+        super.putPersistableBundle(key, value);
+    }
+
+    /**
      * Inserts an {@link IBinder} value into the mapping of this Bundle, replacing
      * any existing value for the given key.  Either key or value may be null.
      *
@@ -817,33 +752,9 @@
      * @param key a String
      * @return a boolean value
      */
+    @Override
     public boolean getBoolean(String key) {
-        unparcel();
-        if (DEBUG) Log.d(TAG, "Getting boolean in "
-                + Integer.toHexString(System.identityHashCode(this)));
-        return getBoolean(key, false);
-    }
-
-    // Log a message if the value was non-null but not of the expected type
-    private void typeWarning(String key, Object value, String className,
-        Object defaultValue, ClassCastException e) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("Key ");
-        sb.append(key);
-        sb.append(" expected ");
-        sb.append(className);
-        sb.append(" but value was a ");
-        sb.append(value.getClass().getName());
-        sb.append(".  The default value ");
-        sb.append(defaultValue);
-        sb.append(" was returned.");
-        Log.w(TAG, sb.toString());
-        Log.w(TAG, "Attempt to cast generated internal exception:", e);
-    }
-
-    private void typeWarning(String key, Object value, String className,
-        ClassCastException e) {
-        typeWarning(key, value, className, "<null>", e);
+        return super.getBoolean(key);
     }
 
     /**
@@ -854,18 +765,9 @@
      * @param defaultValue Value to return if key does not exist
      * @return a boolean value
      */
+    @Override
     public boolean getBoolean(String key, boolean defaultValue) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return defaultValue;
-        }
-        try {
-            return (Boolean) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "Boolean", defaultValue, e);
-            return defaultValue;
-        }
+        return super.getBoolean(key, defaultValue);
     }
 
     /**
@@ -875,9 +777,9 @@
      * @param key a String
      * @return a byte value
      */
+    @Override
     public byte getByte(String key) {
-        unparcel();
-        return getByte(key, (byte) 0);
+        return super.getByte(key);
     }
 
     /**
@@ -888,18 +790,9 @@
      * @param defaultValue Value to return if key does not exist
      * @return a byte value
      */
+    @Override
     public Byte getByte(String key, byte defaultValue) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return defaultValue;
-        }
-        try {
-            return (Byte) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "Byte", defaultValue, e);
-            return defaultValue;
-        }
+        return super.getByte(key, defaultValue);
     }
 
     /**
@@ -909,9 +802,9 @@
      * @param key a String
      * @return a char value
      */
+    @Override
     public char getChar(String key) {
-        unparcel();
-        return getChar(key, (char) 0);
+        return super.getChar(key);
     }
 
     /**
@@ -922,18 +815,9 @@
      * @param defaultValue Value to return if key does not exist
      * @return a char value
      */
+    @Override
     public char getChar(String key, char defaultValue) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return defaultValue;
-        }
-        try {
-            return (Character) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "Character", defaultValue, e);
-            return defaultValue;
-        }
+        return super.getChar(key, defaultValue);
     }
 
     /**
@@ -943,9 +827,9 @@
      * @param key a String
      * @return a short value
      */
+    @Override
     public short getShort(String key) {
-        unparcel();
-        return getShort(key, (short) 0);
+        return super.getShort(key);
     }
 
     /**
@@ -956,18 +840,9 @@
      * @param defaultValue Value to return if key does not exist
      * @return a short value
      */
+    @Override
     public short getShort(String key, short defaultValue) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return defaultValue;
-        }
-        try {
-            return (Short) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "Short", defaultValue, e);
-            return defaultValue;
-        }
+        return super.getShort(key, defaultValue);
     }
 
     /**
@@ -977,9 +852,9 @@
      * @param key a String
      * @return an int value
      */
+    @Override
     public int getInt(String key) {
-        unparcel();
-        return getInt(key, 0);
+        return super.getInt(key);
     }
 
     /**
@@ -990,18 +865,9 @@
      * @param defaultValue Value to return if key does not exist
      * @return an int value
      */
+    @Override
     public int getInt(String key, int defaultValue) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return defaultValue;
-        }
-        try {
-            return (Integer) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "Integer", defaultValue, e);
-            return defaultValue;
-        }
+        return super.getInt(key, defaultValue);
     }
 
     /**
@@ -1011,9 +877,9 @@
      * @param key a String
      * @return a long value
      */
+    @Override
     public long getLong(String key) {
-        unparcel();
-        return getLong(key, 0L);
+        return super.getLong(key);
     }
 
     /**
@@ -1024,18 +890,9 @@
      * @param defaultValue Value to return if key does not exist
      * @return a long value
      */
+    @Override
     public long getLong(String key, long defaultValue) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return defaultValue;
-        }
-        try {
-            return (Long) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "Long", defaultValue, e);
-            return defaultValue;
-        }
+        return super.getLong(key, defaultValue);
     }
 
     /**
@@ -1045,9 +902,9 @@
      * @param key a String
      * @return a float value
      */
+    @Override
     public float getFloat(String key) {
-        unparcel();
-        return getFloat(key, 0.0f);
+        return super.getFloat(key);
     }
 
     /**
@@ -1058,18 +915,9 @@
      * @param defaultValue Value to return if key does not exist
      * @return a float value
      */
+    @Override
     public float getFloat(String key, float defaultValue) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return defaultValue;
-        }
-        try {
-            return (Float) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "Float", defaultValue, e);
-            return defaultValue;
-        }
+        return super.getFloat(key, defaultValue);
     }
 
     /**
@@ -1079,9 +927,9 @@
      * @param key a String
      * @return a double value
      */
+    @Override
     public double getDouble(String key) {
-        unparcel();
-        return getDouble(key, 0.0);
+        return super.getDouble(key);
     }
 
     /**
@@ -1092,18 +940,9 @@
      * @param defaultValue Value to return if key does not exist
      * @return a double value
      */
+    @Override
     public double getDouble(String key, double defaultValue) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return defaultValue;
-        }
-        try {
-            return (Double) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "Double", defaultValue, e);
-            return defaultValue;
-        }
+        return super.getDouble(key, defaultValue);
     }
 
     /**
@@ -1114,15 +953,9 @@
      * @param key a String, or null
      * @return a String value, or null
      */
+    @Override
     public String getString(String key) {
-        unparcel();
-        final Object o = mMap.get(key);
-        try {
-            return (String) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "String", e);
-            return null;
-        }
+        return super.getString(key);
     }
 
     /**
@@ -1134,9 +967,9 @@
      * @return the String value associated with the given key, or defaultValue
      *     if no valid String object is currently mapped to that key.
      */
+    @Override
     public String getString(String key, String defaultValue) {
-        final String s = getString(key);
-        return (s == null) ? defaultValue : s;
+        return super.getString(key, defaultValue);
     }
 
     /**
@@ -1147,15 +980,9 @@
      * @param key a String, or null
      * @return a CharSequence value, or null
      */
+    @Override
     public CharSequence getCharSequence(String key) {
-        unparcel();
-        final Object o = mMap.get(key);
-        try {
-            return (CharSequence) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "CharSequence", e);
-            return null;
-        }
+        return super.getCharSequence(key);
     }
 
     /**
@@ -1167,9 +994,9 @@
      * @return the CharSequence value associated with the given key, or defaultValue
      *     if no valid CharSequence object is currently mapped to that key.
      */
+    @Override
     public CharSequence getCharSequence(String key, CharSequence defaultValue) {
-        final CharSequence cs = getCharSequence(key);
-        return (cs == null) ? defaultValue : cs;
+        return super.getCharSequence(key, defaultValue);
     }
 
     /**
@@ -1200,6 +1027,18 @@
      * value is explicitly associated with the key.
      *
      * @param key a String, or null
+     * @return a PersistableBundle value, or null
+     */
+    public PersistableBundle getPersistableBundle(String key) {
+        return super.getPersistableBundle(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
      * @return a Parcelable value, or null
      */
     public <T extends Parcelable> T getParcelable(String key) {
@@ -1291,18 +1130,9 @@
      * @param key a String, or null
      * @return a Serializable value, or null
      */
+    @Override
     public Serializable getSerializable(String key) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (Serializable) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "Serializable", e);
-            return null;
-        }
+        return super.getSerializable(key);
     }
 
     /**
@@ -1313,18 +1143,9 @@
      * @param key a String, or null
      * @return an ArrayList<String> value, or null
      */
+    @Override
     public ArrayList<Integer> getIntegerArrayList(String key) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (ArrayList<Integer>) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "ArrayList<Integer>", e);
-            return null;
-        }
+        return super.getIntegerArrayList(key);
     }
 
     /**
@@ -1335,18 +1156,9 @@
      * @param key a String, or null
      * @return an ArrayList<String> value, or null
      */
+    @Override
     public ArrayList<String> getStringArrayList(String key) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (ArrayList<String>) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "ArrayList<String>", e);
-            return null;
-        }
+        return super.getStringArrayList(key);
     }
 
     /**
@@ -1357,18 +1169,9 @@
      * @param key a String, or null
      * @return an ArrayList<CharSequence> value, or null
      */
+    @Override
     public ArrayList<CharSequence> getCharSequenceArrayList(String key) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (ArrayList<CharSequence>) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "ArrayList<CharSequence>", e);
-            return null;
-        }
+        return super.getCharSequenceArrayList(key);
     }
 
     /**
@@ -1379,18 +1182,9 @@
      * @param key a String, or null
      * @return a boolean[] value, or null
      */
+    @Override
     public boolean[] getBooleanArray(String key) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (boolean[]) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "byte[]", e);
-            return null;
-        }
+        return super.getBooleanArray(key);
     }
 
     /**
@@ -1401,18 +1195,9 @@
      * @param key a String, or null
      * @return a byte[] value, or null
      */
+    @Override
     public byte[] getByteArray(String key) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (byte[]) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "byte[]", e);
-            return null;
-        }
+        return super.getByteArray(key);
     }
 
     /**
@@ -1423,18 +1208,9 @@
      * @param key a String, or null
      * @return a short[] value, or null
      */
+    @Override
     public short[] getShortArray(String key) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (short[]) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "short[]", e);
-            return null;
-        }
+        return super.getShortArray(key);
     }
 
     /**
@@ -1445,18 +1221,9 @@
      * @param key a String, or null
      * @return a char[] value, or null
      */
+    @Override
     public char[] getCharArray(String key) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (char[]) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "char[]", e);
-            return null;
-        }
+        return super.getCharArray(key);
     }
 
     /**
@@ -1467,18 +1234,9 @@
      * @param key a String, or null
      * @return an int[] value, or null
      */
+    @Override
     public int[] getIntArray(String key) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (int[]) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "int[]", e);
-            return null;
-        }
+        return super.getIntArray(key);
     }
 
     /**
@@ -1489,18 +1247,9 @@
      * @param key a String, or null
      * @return a long[] value, or null
      */
+    @Override
     public long[] getLongArray(String key) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (long[]) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "long[]", e);
-            return null;
-        }
+        return super.getLongArray(key);
     }
 
     /**
@@ -1511,18 +1260,9 @@
      * @param key a String, or null
      * @return a float[] value, or null
      */
+    @Override
     public float[] getFloatArray(String key) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (float[]) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "float[]", e);
-            return null;
-        }
+        return super.getFloatArray(key);
     }
 
     /**
@@ -1533,18 +1273,9 @@
      * @param key a String, or null
      * @return a double[] value, or null
      */
+    @Override
     public double[] getDoubleArray(String key) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (double[]) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "double[]", e);
-            return null;
-        }
+        return super.getDoubleArray(key);
     }
 
     /**
@@ -1555,18 +1286,9 @@
      * @param key a String, or null
      * @return a String[] value, or null
      */
+    @Override
     public String[] getStringArray(String key) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (String[]) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "String[]", e);
-            return null;
-        }
+        return super.getStringArray(key);
     }
 
     /**
@@ -1577,18 +1299,9 @@
      * @param key a String, or null
      * @return a CharSequence[] value, or null
      */
+    @Override
     public CharSequence[] getCharSequenceArray(String key) {
-        unparcel();
-        Object o = mMap.get(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (CharSequence[]) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "CharSequence[]", e);
-            return null;
-        }
+        return super.getCharSequenceArray(key);
     }
 
     /**
@@ -1641,10 +1354,12 @@
 
     public static final Parcelable.Creator<Bundle> CREATOR =
         new Parcelable.Creator<Bundle>() {
+        @Override
         public Bundle createFromParcel(Parcel in) {
             return in.readBundle();
         }
 
+        @Override
         public Bundle[] newArray(int size) {
             return new Bundle[size];
         }
@@ -1653,6 +1368,7 @@
     /**
      * Report the nature of this Parcelable's contents
      */
+    @Override
     public int describeContents() {
         int mask = 0;
         if (hasFileDescriptors()) {
@@ -1660,44 +1376,17 @@
         }
         return mask;
     }
-    
+
     /**
      * Writes the Bundle contents to a Parcel, typically in order for
      * it to be passed through an IBinder connection.
      * @param parcel The parcel to copy this bundle to.
      */
+    @Override
     public void writeToParcel(Parcel parcel, int flags) {
         final boolean oldAllowFds = parcel.pushAllowFds(mAllowFds);
         try {
-            if (mParcelledData != null) {
-                if (mParcelledData == EMPTY_PARCEL) {
-                    parcel.writeInt(0);
-                } else {
-                    int length = mParcelledData.dataSize();
-                    parcel.writeInt(length);
-                    parcel.writeInt(BUNDLE_MAGIC);
-                    parcel.appendFrom(mParcelledData, 0, length);
-                }
-            } else {
-                // Special case for empty bundles.
-                if (mMap == null || mMap.size() <= 0) {
-                    parcel.writeInt(0);
-                    return;
-                }
-                int lengthPos = parcel.dataPosition();
-                parcel.writeInt(-1); // dummy, will hold length
-                parcel.writeInt(BUNDLE_MAGIC);
-    
-                int startPos = parcel.dataPosition();
-                parcel.writeArrayMapInternal(mMap);
-                int endPos = parcel.dataPosition();
-    
-                // Backpatch length
-                parcel.setDataPosition(lengthPos);
-                int length = endPos - startPos;
-                parcel.writeInt(length);
-                parcel.setDataPosition(endPos);
-            }
+            super.writeToParcelInner(parcel, flags);
         } finally {
             parcel.restoreAllowFds(oldAllowFds);
         }
@@ -1709,41 +1398,8 @@
      * @param parcel The parcel to overwrite this bundle from.
      */
     public void readFromParcel(Parcel parcel) {
-        int length = parcel.readInt();
-        if (length < 0) {
-            throw new RuntimeException("Bad length in parcel: " + length);
-        }
-        readFromParcelInner(parcel, length);
-    }
-
-    void readFromParcelInner(Parcel parcel, int length) {
-        if (length == 0) {
-            // Empty Bundle or end of data.
-            mParcelledData = EMPTY_PARCEL;
-            mHasFds = false;
-            mFdsKnown = true;
-            return;
-        }
-        int magic = parcel.readInt();
-        if (magic != BUNDLE_MAGIC) {
-            //noinspection ThrowableInstanceNeverThrown
-            throw new IllegalStateException("Bad magic number for Bundle: 0x"
-                    + Integer.toHexString(magic));
-        }
-
-        // Advance within this Parcel
-        int offset = parcel.dataPosition();
-        parcel.setDataPosition(offset + length);
-
-        Parcel p = Parcel.obtain();
-        p.setDataPosition(0);
-        p.appendFrom(parcel, offset, length);
-        if (DEBUG) Log.d(TAG, "Retrieving "  + Integer.toHexString(System.identityHashCode(this))
-                + ": " + length + " bundle bytes starting at " + offset);
-        p.setDataPosition(0);
-
-        mParcelledData = p;
-        mHasFds = p.hasFileDescriptors();
+        super.readFromParcelInner(parcel);
+        mHasFds = mParcelledData.hasFileDescriptors();
         mFdsKnown = true;
     }
 
@@ -1759,4 +1415,5 @@
         }
         return "Bundle[" + mMap.toString() + "]";
     }
+
 }
diff --git a/core/java/android/os/CommonBundle.java b/core/java/android/os/CommonBundle.java
new file mode 100644
index 0000000..e11f170
--- /dev/null
+++ b/core/java/android/os/CommonBundle.java
@@ -0,0 +1,1384 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A mapping from String values to various types.
+ */
+abstract class CommonBundle implements Parcelable, Cloneable {
+    private static final String TAG = "Bundle";
+    static final boolean DEBUG = false;
+
+    static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
+    static final Parcel EMPTY_PARCEL;
+
+    static {
+        EMPTY_PARCEL = Parcel.obtain();
+    }
+
+    // Invariant - exactly one of mMap / mParcelledData will be null
+    // (except inside a call to unparcel)
+
+    ArrayMap<String, Object> mMap = null;
+
+    /*
+     * If mParcelledData is non-null, then mMap will be null and the
+     * data are stored as a Parcel containing a Bundle.  When the data
+     * are unparcelled, mParcelledData willbe set to null.
+     */
+    Parcel mParcelledData = null;
+
+    /**
+     * The ClassLoader used when unparcelling data from mParcelledData.
+     */
+    private ClassLoader mClassLoader;
+
+    /**
+     * Constructs a new, empty Bundle that uses a specific ClassLoader for
+     * instantiating Parcelable and Serializable objects.
+     *
+     * @param loader An explicit ClassLoader to use when instantiating objects
+     * inside of the Bundle.
+     * @param capacity Initial size of the ArrayMap.
+     */
+    CommonBundle(ClassLoader loader, int capacity) {
+        mMap = capacity > 0 ?
+                new ArrayMap<String, Object>(capacity) : new ArrayMap<String, Object>();
+        mClassLoader = loader == null ? getClass().getClassLoader() : loader;
+    }
+
+    /**
+     * Constructs a new, empty Bundle.
+     */
+    CommonBundle() {
+        this((ClassLoader) null, 0);
+    }
+
+    /**
+     * Constructs a Bundle whose data is stored as a Parcel.  The data
+     * will be unparcelled on first contact, using the assigned ClassLoader.
+     *
+     * @param parcelledData a Parcel containing a Bundle
+     */
+    CommonBundle(Parcel parcelledData) {
+        readFromParcelInner(parcelledData);
+    }
+
+    CommonBundle(Parcel parcelledData, int length) {
+        readFromParcelInner(parcelledData, length);
+    }
+
+    /**
+     * Constructs a new, empty Bundle that uses a specific ClassLoader for
+     * instantiating Parcelable and Serializable objects.
+     *
+     * @param loader An explicit ClassLoader to use when instantiating objects
+     * inside of the Bundle.
+     */
+    CommonBundle(ClassLoader loader) {
+        this(loader, 0);
+    }
+
+    /**
+     * Constructs a new, empty Bundle sized to hold the given number of
+     * elements. The Bundle will grow as needed.
+     *
+     * @param capacity the initial capacity of the Bundle
+     */
+    CommonBundle(int capacity) {
+        this((ClassLoader) null, capacity);
+    }
+
+    /**
+     * Constructs a Bundle containing a copy of the mappings from the given
+     * Bundle.
+     *
+     * @param b a Bundle to be copied.
+     */
+    CommonBundle(CommonBundle b) {
+        if (b.mParcelledData != null) {
+            if (b.mParcelledData == EMPTY_PARCEL) {
+                mParcelledData = EMPTY_PARCEL;
+            } else {
+                mParcelledData = Parcel.obtain();
+                mParcelledData.appendFrom(b.mParcelledData, 0, b.mParcelledData.dataSize());
+                mParcelledData.setDataPosition(0);
+            }
+        } else {
+            mParcelledData = null;
+        }
+
+        if (b.mMap != null) {
+            mMap = new ArrayMap<String, Object>(b.mMap);
+        } else {
+            mMap = null;
+        }
+
+        mClassLoader = b.mClassLoader;
+    }
+
+    /**
+     * TODO: optimize this later (getting just the value part of a Bundle
+     * with a single pair) once Bundle.forPair() above is implemented
+     * with a special single-value Map implementation/serialization.
+     *
+     * Note: value in single-pair Bundle may be null.
+     *
+     * @hide
+     */
+    String getPairValue() {
+        unparcel();
+        int size = mMap.size();
+        if (size > 1) {
+            Log.w(TAG, "getPairValue() used on Bundle with multiple pairs.");
+        }
+        if (size == 0) {
+            return null;
+        }
+        Object o = mMap.valueAt(0);
+        try {
+            return (String) o;
+        } catch (ClassCastException e) {
+            typeWarning("getPairValue()", o, "String", e);
+            return null;
+        }
+    }
+
+    /**
+     * Changes the ClassLoader this Bundle uses when instantiating objects.
+     *
+     * @param loader An explicit ClassLoader to use when instantiating objects
+     * inside of the Bundle.
+     */
+    void setClassLoader(ClassLoader loader) {
+        mClassLoader = loader;
+    }
+
+    /**
+     * Return the ClassLoader currently associated with this Bundle.
+     */
+    ClassLoader getClassLoader() {
+        return mClassLoader;
+    }
+
+    /**
+     * If the underlying data are stored as a Parcel, unparcel them
+     * using the currently assigned class loader.
+     */
+    /* package */ synchronized void unparcel() {
+        if (mParcelledData == null) {
+            if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+                    + ": no parcelled data");
+            return;
+        }
+
+        if (mParcelledData == EMPTY_PARCEL) {
+            if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+                    + ": empty");
+            if (mMap == null) {
+                mMap = new ArrayMap<String, Object>(1);
+            } else {
+                mMap.erase();
+            }
+            mParcelledData = null;
+            return;
+        }
+
+        int N = mParcelledData.readInt();
+        if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+                + ": reading " + N + " maps");
+        if (N < 0) {
+            return;
+        }
+        if (mMap == null) {
+            mMap = new ArrayMap<String, Object>(N);
+        } else {
+            mMap.erase();
+            mMap.ensureCapacity(N);
+        }
+        mParcelledData.readArrayMapInternal(mMap, N, mClassLoader);
+        mParcelledData.recycle();
+        mParcelledData = null;
+        if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+                + " final map: " + mMap);
+    }
+
+    /**
+     * @hide
+     */
+    boolean isParcelled() {
+        return mParcelledData != null;
+    }
+
+    /**
+     * Returns the number of mappings contained in this Bundle.
+     *
+     * @return the number of mappings as an int.
+     */
+    int size() {
+        unparcel();
+        return mMap.size();
+    }
+
+    /**
+     * Returns true if the mapping of this Bundle is empty, false otherwise.
+     */
+    boolean isEmpty() {
+        unparcel();
+        return mMap.isEmpty();
+    }
+
+    /**
+     * Removes all elements from the mapping of this Bundle.
+     */
+    void clear() {
+        unparcel();
+        mMap.clear();
+    }
+
+    /**
+     * Returns true if the given key is contained in the mapping
+     * of this Bundle.
+     *
+     * @param key a String key
+     * @return true if the key is part of the mapping, false otherwise
+     */
+    boolean containsKey(String key) {
+        unparcel();
+        return mMap.containsKey(key);
+    }
+
+    /**
+     * Returns the entry with the given key as an object.
+     *
+     * @param key a String key
+     * @return an Object, or null
+     */
+    Object get(String key) {
+        unparcel();
+        return mMap.get(key);
+    }
+
+    /**
+     * Removes any entry with the given key from the mapping of this Bundle.
+     *
+     * @param key a String key
+     */
+    void remove(String key) {
+        unparcel();
+        mMap.remove(key);
+    }
+
+    /**
+     * Inserts all mappings from the given PersistableBundle into this CommonBundle.
+     *
+     * @param bundle a PersistableBundle
+     */
+    void putAll(PersistableBundle bundle) {
+        unparcel();
+        bundle.unparcel();
+        mMap.putAll(bundle.mMap);
+    }
+
+    /**
+     * Returns a Set containing the Strings used as keys in this Bundle.
+     *
+     * @return a Set of String keys
+     */
+    Set<String> keySet() {
+        unparcel();
+        return mMap.keySet();
+    }
+
+    /**
+     * Inserts a Boolean value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a Boolean, or null
+     */
+    void putBoolean(String key, boolean value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a byte value into the mapping of this Bundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value a byte
+     */
+    void putByte(String key, byte value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a char value into the mapping of this Bundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value a char, or null
+     */
+    void putChar(String key, char value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a short value into the mapping of this Bundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value a short
+     */
+    void putShort(String key, short value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts an int value into the mapping of this Bundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value an int, or null
+     */
+    void putInt(String key, int value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a long value into the mapping of this Bundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value a long
+     */
+    void putLong(String key, long value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a float value into the mapping of this Bundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value a float
+     */
+    void putFloat(String key, float value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a double value into the mapping of this Bundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value a double
+     */
+    void putDouble(String key, double value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a String value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a String, or null
+     */
+    void putString(String key, String value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a CharSequence value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a CharSequence, or null
+     */
+    void putCharSequence(String key, CharSequence value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts an ArrayList<Integer> value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value an ArrayList<Integer> object, or null
+     */
+    void putIntegerArrayList(String key, ArrayList<Integer> value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts an ArrayList<String> value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value an ArrayList<String> object, or null
+     */
+    void putStringArrayList(String key, ArrayList<String> value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts an ArrayList<CharSequence> value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value an ArrayList<CharSequence> object, or null
+     */
+    void putCharSequenceArrayList(String key, ArrayList<CharSequence> value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a Serializable value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a Serializable object, or null
+     */
+    void putSerializable(String key, Serializable value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a boolean array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a boolean array object, or null
+     */
+    void putBooleanArray(String key, boolean[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a byte array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a byte array object, or null
+     */
+    void putByteArray(String key, byte[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a short array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a short array object, or null
+     */
+    void putShortArray(String key, short[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a char array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a char array object, or null
+     */
+    void putCharArray(String key, char[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts an int array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value an int array object, or null
+     */
+    void putIntArray(String key, int[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a long array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a long array object, or null
+     */
+    void putLongArray(String key, long[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a float array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a float array object, or null
+     */
+    void putFloatArray(String key, float[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a double array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a double array object, or null
+     */
+    void putDoubleArray(String key, double[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a String array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a String array object, or null
+     */
+    void putStringArray(String key, String[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a CharSequence array value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a CharSequence array object, or null
+     */
+    void putCharSequenceArray(String key, CharSequence[] value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Inserts a PersistableBundle value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a Bundle object, or null
+     */
+    void putPersistableBundle(String key, PersistableBundle value) {
+        unparcel();
+        mMap.put(key, value);
+    }
+
+    /**
+     * Returns the value associated with the given key, or false if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a boolean value
+     */
+    boolean getBoolean(String key) {
+        unparcel();
+        if (DEBUG) Log.d(TAG, "Getting boolean in "
+                + Integer.toHexString(System.identityHashCode(this)));
+        return getBoolean(key, false);
+    }
+
+    // Log a message if the value was non-null but not of the expected type
+    void typeWarning(String key, Object value, String className,
+            Object defaultValue, ClassCastException e) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Key ");
+        sb.append(key);
+        sb.append(" expected ");
+        sb.append(className);
+        sb.append(" but value was a ");
+        sb.append(value.getClass().getName());
+        sb.append(".  The default value ");
+        sb.append(defaultValue);
+        sb.append(" was returned.");
+        Log.w(TAG, sb.toString());
+        Log.w(TAG, "Attempt to cast generated internal exception:", e);
+    }
+
+    void typeWarning(String key, Object value, String className,
+            ClassCastException e) {
+        typeWarning(key, value, className, "<null>", e);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a boolean value
+     */
+    boolean getBoolean(String key, boolean defaultValue) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return defaultValue;
+        }
+        try {
+            return (Boolean) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Boolean", defaultValue, e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or (byte) 0 if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a byte value
+     */
+    byte getByte(String key) {
+        unparcel();
+        return getByte(key, (byte) 0);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a byte value
+     */
+    Byte getByte(String key, byte defaultValue) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return defaultValue;
+        }
+        try {
+            return (Byte) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Byte", defaultValue, e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or (char) 0 if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a char value
+     */
+    char getChar(String key) {
+        unparcel();
+        return getChar(key, (char) 0);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a char value
+     */
+    char getChar(String key, char defaultValue) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return defaultValue;
+        }
+        try {
+            return (Character) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Character", defaultValue, e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or (short) 0 if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a short value
+     */
+    short getShort(String key) {
+        unparcel();
+        return getShort(key, (short) 0);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a short value
+     */
+    short getShort(String key, short defaultValue) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return defaultValue;
+        }
+        try {
+            return (Short) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Short", defaultValue, e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or 0 if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return an int value
+     */
+    int getInt(String key) {
+        unparcel();
+        return getInt(key, 0);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return an int value
+     */
+    int getInt(String key, int defaultValue) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return defaultValue;
+        }
+        try {
+            return (Integer) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Integer", defaultValue, e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or 0L if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a long value
+     */
+    long getLong(String key) {
+        unparcel();
+        return getLong(key, 0L);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a long value
+     */
+    long getLong(String key, long defaultValue) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return defaultValue;
+        }
+        try {
+            return (Long) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Long", defaultValue, e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or 0.0f if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a float value
+     */
+    float getFloat(String key) {
+        unparcel();
+        return getFloat(key, 0.0f);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a float value
+     */
+    float getFloat(String key, float defaultValue) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return defaultValue;
+        }
+        try {
+            return (Float) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Float", defaultValue, e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or 0.0 if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a double value
+     */
+    double getDouble(String key) {
+        unparcel();
+        return getDouble(key, 0.0);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a double value
+     */
+    double getDouble(String key, double defaultValue) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return defaultValue;
+        }
+        try {
+            return (Double) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Double", defaultValue, e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a String value, or null
+     */
+    String getString(String key) {
+        unparcel();
+        final Object o = mMap.get(key);
+        try {
+            return (String) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "String", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String, or null
+     * @param defaultValue Value to return if key does not exist
+     * @return the String value associated with the given key, or defaultValue
+     *     if no valid String object is currently mapped to that key.
+     */
+    String getString(String key, String defaultValue) {
+        final String s = getString(key);
+        return (s == null) ? defaultValue : s;
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a CharSequence value, or null
+     */
+    CharSequence getCharSequence(String key) {
+        unparcel();
+        final Object o = mMap.get(key);
+        try {
+            return (CharSequence) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "CharSequence", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String, or null
+     * @param defaultValue Value to return if key does not exist
+     * @return the CharSequence value associated with the given key, or defaultValue
+     *     if no valid CharSequence object is currently mapped to that key.
+     */
+    CharSequence getCharSequence(String key, CharSequence defaultValue) {
+        final CharSequence cs = getCharSequence(key);
+        return (cs == null) ? defaultValue : cs;
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a Bundle value, or null
+     */
+    PersistableBundle getPersistableBundle(String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (PersistableBundle) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Bundle", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a Serializable value, or null
+     */
+    Serializable getSerializable(String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (Serializable) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "Serializable", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return an ArrayList<String> value, or null
+     */
+    ArrayList<Integer> getIntegerArrayList(String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (ArrayList<Integer>) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "ArrayList<Integer>", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return an ArrayList<String> value, or null
+     */
+    ArrayList<String> getStringArrayList(String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (ArrayList<String>) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "ArrayList<String>", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return an ArrayList<CharSequence> value, or null
+     */
+    ArrayList<CharSequence> getCharSequenceArrayList(String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (ArrayList<CharSequence>) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "ArrayList<CharSequence>", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a boolean[] value, or null
+     */
+    boolean[] getBooleanArray(String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (boolean[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "byte[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a byte[] value, or null
+     */
+    byte[] getByteArray(String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (byte[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "byte[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a short[] value, or null
+     */
+    short[] getShortArray(String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (short[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "short[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a char[] value, or null
+     */
+    char[] getCharArray(String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (char[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "char[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return an int[] value, or null
+     */
+    int[] getIntArray(String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (int[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "int[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a long[] value, or null
+     */
+    long[] getLongArray(String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (long[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "long[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a float[] value, or null
+     */
+    float[] getFloatArray(String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (float[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "float[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a double[] value, or null
+     */
+    double[] getDoubleArray(String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (double[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "double[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a String[] value, or null
+     */
+    String[] getStringArray(String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (String[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "String[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a CharSequence[] value, or null
+     */
+    CharSequence[] getCharSequenceArray(String key) {
+        unparcel();
+        Object o = mMap.get(key);
+        if (o == null) {
+            return null;
+        }
+        try {
+            return (CharSequence[]) o;
+        } catch (ClassCastException e) {
+            typeWarning(key, o, "CharSequence[]", e);
+            return null;
+        }
+    }
+
+    /**
+     * Writes the Bundle contents to a Parcel, typically in order for
+     * it to be passed through an IBinder connection.
+     * @param parcel The parcel to copy this bundle to.
+     */
+    void writeToParcelInner(Parcel parcel, int flags) {
+        if (mParcelledData != null) {
+            if (mParcelledData == EMPTY_PARCEL) {
+                parcel.writeInt(0);
+            } else {
+                int length = mParcelledData.dataSize();
+                parcel.writeInt(length);
+                parcel.writeInt(BUNDLE_MAGIC);
+                parcel.appendFrom(mParcelledData, 0, length);
+            }
+        } else {
+            // Special case for empty bundles.
+            if (mMap == null || mMap.size() <= 0) {
+                parcel.writeInt(0);
+                return;
+            }
+            int lengthPos = parcel.dataPosition();
+            parcel.writeInt(-1); // dummy, will hold length
+            parcel.writeInt(BUNDLE_MAGIC);
+
+            int startPos = parcel.dataPosition();
+            parcel.writeArrayMapInternal(mMap);
+            int endPos = parcel.dataPosition();
+
+            // Backpatch length
+            parcel.setDataPosition(lengthPos);
+            int length = endPos - startPos;
+            parcel.writeInt(length);
+            parcel.setDataPosition(endPos);
+        }
+    }
+
+    /**
+     * Reads the Parcel contents into this Bundle, typically in order for
+     * it to be passed through an IBinder connection.
+     * @param parcel The parcel to overwrite this bundle from.
+     */
+    void readFromParcelInner(Parcel parcel) {
+        int length = parcel.readInt();
+        if (length < 0) {
+            throw new RuntimeException("Bad length in parcel: " + length);
+        }
+        readFromParcelInner(parcel, length);
+    }
+
+    private void readFromParcelInner(Parcel parcel, int length) {
+        if (length == 0) {
+            // Empty Bundle or end of data.
+            mParcelledData = EMPTY_PARCEL;
+            return;
+        }
+        int magic = parcel.readInt();
+        if (magic != BUNDLE_MAGIC) {
+            //noinspection ThrowableInstanceNeverThrown
+            throw new IllegalStateException("Bad magic number for Bundle: 0x"
+                    + Integer.toHexString(magic));
+        }
+
+        // Advance within this Parcel
+        int offset = parcel.dataPosition();
+        parcel.setDataPosition(offset + length);
+
+        Parcel p = Parcel.obtain();
+        p.setDataPosition(0);
+        p.appendFrom(parcel, offset, length);
+        if (DEBUG) Log.d(TAG, "Retrieving "  + Integer.toHexString(System.identityHashCode(this))
+                + ": " + length + " bundle bytes starting at " + offset);
+        p.setDataPosition(0);
+
+        mParcelledData = p;
+    }
+}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 8e0ff08..95cb9f3 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -223,6 +223,7 @@
     private static final int VAL_SPARSEBOOLEANARRAY = 22;
     private static final int VAL_BOOLEANARRAY = 23;
     private static final int VAL_CHARSEQUENCEARRAY = 24;
+    private static final int VAL_PERSISTABLEBUNDLE = 25;
 
     // The initial int32 in a Binder call's reply Parcel header:
     private static final int EX_SECURITY = -1;
@@ -638,6 +639,19 @@
     }
 
     /**
+     * Flatten a PersistableBundle into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.
+     */
+    public final void writePersistableBundle(PersistableBundle val) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+
+        val.writeToParcel(this, 0);
+    }
+
+    /**
      * Flatten a List into the parcel at the current dataPosition(), growing
      * dataCapacity() if needed.  The List values are written using
      * {@link #writeValue} and must follow the specification there.
@@ -1256,6 +1270,9 @@
         } else if (v instanceof Byte) {
             writeInt(VAL_BYTE);
             writeInt((Byte) v);
+        } else if (v instanceof PersistableBundle) {
+            writeInt(VAL_PERSISTABLEBUNDLE);
+            writePersistableBundle((PersistableBundle) v);
         } else {
             Class<?> clazz = v.getClass();
             if (clazz.isArray() && clazz.getComponentType() == Object.class) {
@@ -1633,6 +1650,35 @@
     }
 
     /**
+     * Read and return a new Bundle object from the parcel at the current
+     * dataPosition().  Returns null if the previously written Bundle object was
+     * null.
+     */
+    public final PersistableBundle readPersistableBundle() {
+        return readPersistableBundle(null);
+    }
+
+    /**
+     * Read and return a new Bundle object from the parcel at the current
+     * dataPosition(), using the given class loader to initialize the class
+     * loader of the Bundle for later retrieval of Parcelable objects.
+     * Returns null if the previously written Bundle object was null.
+     */
+    public final PersistableBundle readPersistableBundle(ClassLoader loader) {
+        int length = readInt();
+        if (length < 0) {
+            if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length);
+            return null;
+        }
+
+        final PersistableBundle bundle = new PersistableBundle(this, length);
+        if (loader != null) {
+            bundle.setClassLoader(loader);
+        }
+        return bundle;
+    }
+
+    /**
      * Read and return a byte[] object from the parcel.
      */
     public final byte[] createByteArray() {
@@ -2082,6 +2128,9 @@
         case VAL_BUNDLE:
             return readBundle(loader); // loading will be deferred
 
+        case VAL_PERSISTABLEBUNDLE:
+            return readPersistableBundle(loader);
+
         default:
             int off = dataPosition() - 4;
             throw new RuntimeException(
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 86dc8b4..24bf05e 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -42,6 +42,7 @@
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InterruptedIOException;
 import java.net.DatagramSocket;
 import java.net.Socket;
 import java.nio.ByteOrder;
@@ -698,6 +699,9 @@
             } catch (ErrnoException e) {
                 // Reporting status is best-effort
                 Log.w(TAG, "Failed to report status: " + e);
+            } catch (InterruptedIOException e) {
+                // Reporting status is best-effort
+                Log.w(TAG, "Failed to report status: " + e);
             }
 
         } finally {
@@ -728,6 +732,9 @@
                 Log.d(TAG, "Failed to read status; assuming dead: " + e);
                 return new Status(Status.DEAD);
             }
+        } catch (InterruptedIOException e) {
+            Log.d(TAG, "Failed to read status; assuming dead: " + e);
+            return new Status(Status.DEAD);
         }
     }
 
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
new file mode 100644
index 0000000..c2cd3be
--- /dev/null
+++ b/core/java/android/os/PersistableBundle.java
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.util.ArrayMap;
+
+import java.util.Set;
+
+/**
+ * A mapping from String values to various types that can be saved to persistent and later
+ * restored.
+ *
+ */
+public final class PersistableBundle extends CommonBundle {
+    public static final PersistableBundle EMPTY;
+    static final Parcel EMPTY_PARCEL;
+
+    static {
+        EMPTY = new PersistableBundle();
+        EMPTY.mMap = ArrayMap.EMPTY;
+        EMPTY_PARCEL = CommonBundle.EMPTY_PARCEL;
+    }
+
+    /**
+     * Constructs a new, empty PersistableBundle.
+     */
+    public PersistableBundle() {
+        super();
+    }
+
+    /**
+     * Constructs a PersistableBundle whose data is stored as a Parcel.  The data
+     * will be unparcelled on first contact, using the assigned ClassLoader.
+     *
+     * @param parcelledData a Parcel containing a PersistableBundle
+     */
+    PersistableBundle(Parcel parcelledData) {
+        super(parcelledData);
+    }
+
+    /* package */ PersistableBundle(Parcel parcelledData, int length) {
+        super(parcelledData, length);
+    }
+
+    /**
+     * Constructs a new, empty PersistableBundle that uses a specific ClassLoader for
+     * instantiating Parcelable and Serializable objects.
+     *
+     * @param loader An explicit ClassLoader to use when instantiating objects
+     * inside of the PersistableBundle.
+     */
+    public PersistableBundle(ClassLoader loader) {
+        super(loader);
+    }
+
+    /**
+     * Constructs a new, empty PersistableBundle sized to hold the given number of
+     * elements. The PersistableBundle will grow as needed.
+     *
+     * @param capacity the initial capacity of the PersistableBundle
+     */
+    public PersistableBundle(int capacity) {
+        super(capacity);
+    }
+
+    /**
+     * Constructs a PersistableBundle containing a copy of the mappings from the given
+     * PersistableBundle.
+     *
+     * @param b a PersistableBundle to be copied.
+     */
+    public PersistableBundle(PersistableBundle b) {
+        super(b);
+    }
+
+    /**
+     * Make a PersistableBundle for a single key/value pair.
+     *
+     * @hide
+     */
+    public static PersistableBundle forPair(String key, String value) {
+        PersistableBundle b = new PersistableBundle(1);
+        b.putString(key, value);
+        return b;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public String getPairValue() {
+        return super.getPairValue();
+    }
+
+    /**
+     * Changes the ClassLoader this PersistableBundle uses when instantiating objects.
+     *
+     * @param loader An explicit ClassLoader to use when instantiating objects
+     * inside of the PersistableBundle.
+     */
+    @Override
+    public void setClassLoader(ClassLoader loader) {
+        super.setClassLoader(loader);
+    }
+
+    /**
+     * Return the ClassLoader currently associated with this PersistableBundle.
+     */
+    @Override
+    public ClassLoader getClassLoader() {
+        return super.getClassLoader();
+    }
+
+    /**
+     * Clones the current PersistableBundle. The internal map is cloned, but the keys and
+     * values to which it refers are copied by reference.
+     */
+    @Override
+    public Object clone() {
+        return new PersistableBundle(this);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isParcelled() {
+        return super.isParcelled();
+    }
+
+    /**
+     * Returns the number of mappings contained in this PersistableBundle.
+     *
+     * @return the number of mappings as an int.
+     */
+    @Override
+    public int size() {
+        return super.size();
+    }
+
+    /**
+     * Returns true if the mapping of this PersistableBundle is empty, false otherwise.
+     */
+    @Override
+    public boolean isEmpty() {
+        return super.isEmpty();
+    }
+
+    /**
+     * Removes all elements from the mapping of this PersistableBundle.
+     */
+    @Override
+    public void clear() {
+        super.clear();
+    }
+
+    /**
+     * Returns true if the given key is contained in the mapping
+     * of this PersistableBundle.
+     *
+     * @param key a String key
+     * @return true if the key is part of the mapping, false otherwise
+     */
+    @Override
+    public boolean containsKey(String key) {
+        return super.containsKey(key);
+    }
+
+    /**
+     * Returns the entry with the given key as an object.
+     *
+     * @param key a String key
+     * @return an Object, or null
+     */
+    @Override
+    public Object get(String key) {
+        return super.get(key);
+    }
+
+    /**
+     * Removes any entry with the given key from the mapping of this PersistableBundle.
+     *
+     * @param key a String key
+     */
+    @Override
+    public void remove(String key) {
+        super.remove(key);
+    }
+
+    /**
+     * Inserts all mappings from the given PersistableBundle into this Bundle.
+     *
+     * @param bundle a PersistableBundle
+     */
+    public void putAll(PersistableBundle bundle) {
+        super.putAll(bundle);
+    }
+
+    /**
+     * Returns a Set containing the Strings used as keys in this PersistableBundle.
+     *
+     * @return a Set of String keys
+     */
+    @Override
+    public Set<String> keySet() {
+        return super.keySet();
+    }
+
+    /**
+     * Inserts an int value into the mapping of this PersistableBundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value an int, or null
+     */
+    @Override
+    public void putInt(String key, int value) {
+        super.putInt(key, value);
+    }
+
+    /**
+     * Inserts a long value into the mapping of this PersistableBundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value a long
+     */
+    @Override
+    public void putLong(String key, long value) {
+        super.putLong(key, value);
+    }
+
+    /**
+     * Inserts a double value into the mapping of this PersistableBundle, replacing
+     * any existing value for the given key.
+     *
+     * @param key a String, or null
+     * @param value a double
+     */
+    @Override
+    public void putDouble(String key, double value) {
+        super.putDouble(key, value);
+    }
+
+    /**
+     * Inserts a String value into the mapping of this PersistableBundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a String, or null
+     */
+    @Override
+    public void putString(String key, String value) {
+        super.putString(key, value);
+    }
+
+    /**
+     * Inserts an int array value into the mapping of this PersistableBundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value an int array object, or null
+     */
+    @Override
+    public void putIntArray(String key, int[] value) {
+        super.putIntArray(key, value);
+    }
+
+    /**
+     * Inserts a long array value into the mapping of this PersistableBundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a long array object, or null
+     */
+    @Override
+    public void putLongArray(String key, long[] value) {
+        super.putLongArray(key, value);
+    }
+
+    /**
+     * Inserts a double array value into the mapping of this PersistableBundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a double array object, or null
+     */
+    @Override
+    public void putDoubleArray(String key, double[] value) {
+        super.putDoubleArray(key, value);
+    }
+
+    /**
+     * Inserts a String array value into the mapping of this PersistableBundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a String array object, or null
+     */
+    @Override
+    public void putStringArray(String key, String[] value) {
+        super.putStringArray(key, value);
+    }
+
+    /**
+     * Inserts a PersistableBundle value into the mapping of this Bundle, replacing
+     * any existing value for the given key.  Either key or value may be null.
+     *
+     * @param key a String, or null
+     * @param value a Bundle object, or null
+     */
+    public void putPersistableBundle(String key, PersistableBundle value) {
+        super.putPersistableBundle(key, value);
+    }
+
+    /**
+     * Returns the value associated with the given key, or 0 if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return an int value
+     */
+    @Override
+    public int getInt(String key) {
+        return super.getInt(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return an int value
+     */
+    @Override
+    public int getInt(String key, int defaultValue) {
+        return super.getInt(key, defaultValue);
+    }
+
+    /**
+     * Returns the value associated with the given key, or 0L if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a long value
+     */
+    @Override
+    public long getLong(String key) {
+        return super.getLong(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a long value
+     */
+    @Override
+    public long getLong(String key, long defaultValue) {
+        return super.getLong(key, defaultValue);
+    }
+
+    /**
+     * Returns the value associated with the given key, or 0.0 if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @return a double value
+     */
+    @Override
+    public double getDouble(String key) {
+        return super.getDouble(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a double value
+     */
+    @Override
+    public double getDouble(String key, double defaultValue) {
+        return super.getDouble(key, defaultValue);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a String value, or null
+     */
+    @Override
+    public String getString(String key) {
+        return super.getString(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String, or null
+     * @param defaultValue Value to return if key does not exist
+     * @return the String value associated with the given key, or defaultValue
+     *     if no valid String object is currently mapped to that key.
+     */
+    @Override
+    public String getString(String key, String defaultValue) {
+        return super.getString(key, defaultValue);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a Bundle value, or null
+     */
+    @Override
+    public PersistableBundle getPersistableBundle(String key) {
+        return super.getPersistableBundle(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return an int[] value, or null
+     */
+    @Override
+    public int[] getIntArray(String key) {
+        return super.getIntArray(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a long[] value, or null
+     */
+    @Override
+    public long[] getLongArray(String key) {
+        return super.getLongArray(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a double[] value, or null
+     */
+    @Override
+    public double[] getDoubleArray(String key) {
+        return super.getDoubleArray(key);
+    }
+
+    /**
+     * Returns the value associated with the given key, or null if
+     * no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key.
+     *
+     * @param key a String, or null
+     * @return a String[] value, or null
+     */
+    @Override
+    public String[] getStringArray(String key) {
+        return super.getStringArray(key);
+    }
+
+    public static final Parcelable.Creator<PersistableBundle> CREATOR =
+            new Parcelable.Creator<PersistableBundle>() {
+                @Override
+                public PersistableBundle createFromParcel(Parcel in) {
+                    return in.readPersistableBundle();
+                }
+
+                @Override
+                public PersistableBundle[] newArray(int size) {
+                    return new PersistableBundle[size];
+                }
+            };
+
+    /**
+     * Report the nature of this Parcelable's contents
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Writes the PersistableBundle contents to a Parcel, typically in order for
+     * it to be passed through an IBinder connection.
+     * @param parcel The parcel to copy this bundle to.
+     */
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        final boolean oldAllowFds = parcel.pushAllowFds(false);
+        try {
+            super.writeToParcelInner(parcel, flags);
+        } finally {
+            parcel.restoreAllowFds(oldAllowFds);
+        }
+    }
+
+    /**
+     * Reads the Parcel contents into this PersistableBundle, typically in order for
+     * it to be passed through an IBinder connection.
+     * @param parcel The parcel to overwrite this bundle from.
+     */
+    public void readFromParcel(Parcel parcel) {
+        super.readFromParcelInner(parcel);
+    }
+
+    @Override
+    synchronized public String toString() {
+        if (mParcelledData != null) {
+            if (mParcelledData == EMPTY_PARCEL) {
+                return "PersistableBundle[EMPTY_PARCEL]";
+            } else {
+                return "PersistableBundle[mParcelledData.dataSize=" +
+                        mParcelledData.dataSize() + "]";
+            }
+        }
+        return "PersistableBundle[" + mMap.toString() + "]";
+    }
+
+}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 8aef9bd..7bac3af 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -300,7 +300,8 @@
 
     /**
      * Sets all the user-wide restrictions for this user.
-     * Requires the MANAGE_USERS permission.
+     * Requires the MANAGE_USERS permission or profile/device owner
+     * privileges.
      * @param restrictions the Bundle containing all the restrictions.
      */
     public void setUserRestrictions(Bundle restrictions) {
@@ -309,7 +310,8 @@
 
     /**
      * Sets all the user-wide restrictions for the specified user.
-     * Requires the MANAGE_USERS permission.
+     * Requires the MANAGE_USERS permission or profile/device owner
+     * privileges.
      * @param restrictions the Bundle containing all the restrictions.
      * @param userHandle the UserHandle of the user for whom to set the restrictions.
      */
@@ -323,7 +325,8 @@
 
     /**
      * Sets the value of a specific restriction.
-     * Requires the MANAGE_USERS permission.
+     * Requires the MANAGE_USERS permission or profile/device owner
+     * privileges.
      * @param key the key of the restriction
      * @param value the value for the restriction
      */
@@ -336,7 +339,8 @@
     /**
      * @hide
      * Sets the value of a specific restriction on a specific user.
-     * Requires the {@link android.Manifest.permission#MANAGE_USERS} permission.
+     * Requires the {@link android.Manifest.permission#MANAGE_USERS} permission or profile/device owner
+     * privileges.
      * @param key the key of the restriction
      * @param value the value for the restriction
      * @param userHandle the user whose restriction is to be changed.
@@ -548,7 +552,7 @@
     /**
      * Returns information for all users on this device. Requires
      * {@link android.Manifest.permission#MANAGE_USERS} permission.
-     * 
+     *
      * @param excludeDying specify if the list should exclude users being
      *            removed.
      * @return the list of users that were created.
diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java
index 13cc88b..2cc91b9 100644
--- a/core/java/android/util/Patterns.java
+++ b/core/java/android/util/Patterns.java
@@ -130,7 +130,10 @@
     private static final String IRI
         = "[" + GOOD_IRI_CHAR + "]([" + GOOD_IRI_CHAR + "\\-]{0,61}[" + GOOD_IRI_CHAR + "]){0,1}";
 
-    private static final String HOST_NAME = IRI + "(?:\\." + IRI + ")+";
+    private static final String GOOD_GTLD_CHAR =
+        "a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF";
+    private static final String GTLD = "[" + GOOD_GTLD_CHAR + "]{2,63}";
+    private static final String HOST_NAME = "(" + IRI + "\\.)+" + GTLD;
 
     public static final Pattern DOMAIN_NAME
         = Pattern.compile("(" + HOST_NAME + "|" + IP_ADDRESS + ")");
diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java
index a7ee12b..71296fa 100644
--- a/core/java/android/view/MenuInflater.java
+++ b/core/java/android/view/MenuInflater.java
@@ -161,6 +161,7 @@
                     } else if (tagName.equals(XML_MENU)) {
                         // A menu start tag denotes a submenu for an item
                         SubMenu subMenu = menuState.addSubMenuItem();
+                        registerMenu(subMenu, attrs);
 
                         // Parse the submenu into returned SubMenu
                         parseMenu(parser, attrs, subMenu);
@@ -183,9 +184,9 @@
                         if (!menuState.hasAddedItem()) {
                             if (menuState.itemActionProvider != null &&
                                     menuState.itemActionProvider.hasSubMenu()) {
-                                menuState.addSubMenuItem();
+                                registerMenu(menuState.addSubMenuItem(), attrs);
                             } else {
-                                menuState.addItem();
+                                registerMenu(menuState.addItem(), attrs);
                             }
                         }
                     } else if (tagName.equals(XML_MENU)) {
@@ -200,7 +201,30 @@
             eventType = parser.next();
         }
     }
-    
+
+    /**
+     * The method is a hook for layoutlib to do its magic.
+     * Nothing is needed outside of LayoutLib. However, it should not be deleted because it
+     * appears to do nothing.
+     */
+    private void registerMenu(@SuppressWarnings("unused") MenuItem item,
+            @SuppressWarnings("unused") AttributeSet set) {
+    }
+
+    /**
+     * The method is a hook for layoutlib to do its magic.
+     * Nothing is needed outside of LayoutLib. However, it should not be deleted because it
+     * appears to do nothing.
+     */
+    private void registerMenu(@SuppressWarnings("unused") SubMenu subMenu,
+            @SuppressWarnings("unused") AttributeSet set) {
+    }
+
+    // Needed by layoutlib.
+    /*package*/ Context getContext() {
+        return mContext;
+    }
+
     private static class InflatedOnMenuItemClickListener
             implements MenuItem.OnMenuItemClickListener {
         private static final Class<?>[] PARAM_TYPES = new Class[] { MenuItem.class };
@@ -446,9 +470,11 @@
             }
         }
 
-        public void addItem() {
+        public MenuItem addItem() {
             itemAdded = true;
-            setItem(menu.add(groupId, itemId, itemCategoryOrder, itemTitle));
+            MenuItem item = menu.add(groupId, itemId, itemCategoryOrder, itemTitle);
+            setItem(item);
+            return item;
         }
         
         public SubMenu addSubMenuItem() {
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 8a30def..225cd6d 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -314,7 +314,7 @@
         final int right = left + thumbWidth;
 
         final Drawable background = getBackground();
-        if (background.supportsHotspots()) {
+        if (background != null && background.supportsHotspots()) {
             final Rect bounds = mThumb.getBounds();
             final int offsetX = mPaddingLeft - mThumbOffset;
             final int offsetY = mPaddingTop;
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index ddc8b05..9e17cca 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -285,7 +285,7 @@
             buttonDrawable.setBounds(left, top, right, bottom);
 
             final Drawable background = getBackground();
-            if (background.supportsHotspots()) {
+            if (background != null && background.supportsHotspots()) {
                 background.setHotspotBounds(left, top, right, bottom);
             }
         }
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index d013e7b..08af4de 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -799,7 +799,7 @@
         thumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);
 
         final Drawable background = getBackground();
-        if (background.supportsHotspots()) {
+        if (background != null && background.supportsHotspots()) {
             background.setHotspotBounds(thumbLeft, switchTop, thumbRight, switchBottom);
         }
 
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index b776226..5d7d322 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -392,8 +392,8 @@
     private MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) {
         final int ordering = getOrdering(categoryOrder);
         
-        final MenuItemImpl item = new MenuItemImpl(this, group, id, categoryOrder,
-                ordering, title, mDefaultShowAsAction);
+        final MenuItemImpl item = createNewMenuItem(group, id, categoryOrder, ordering, title,
+                mDefaultShowAsAction);
 
         if (mCurrentMenuInfo != null) {
             // Pass along the current menu info
@@ -405,7 +405,14 @@
         
         return item;
     }
-    
+
+    // Layoutlib overrides this method to return its custom implementation of MenuItemImpl
+    private MenuItemImpl createNewMenuItem(int group, int id, int categoryOrder, int ordering,
+            CharSequence title, int defaultShowAsAction) {
+        return new MenuItemImpl(this, group, id, categoryOrder, ordering, title,
+                defaultShowAsAction);
+    }
+
     public MenuItem add(CharSequence title) {
         return addInternal(0, 0, 0, title);
     }
diff --git a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
index 1958cfe..5101e35 100644
--- a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
+++ b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
@@ -25,12 +25,13 @@
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff.Mode;
-import android.graphics.drawable.Ripple.RippleAnimator;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.graphics.drawable.Ripple.RippleAnimator;
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.internal.R;
@@ -45,6 +46,7 @@
  * Documentation pending.
  */
 public class TouchFeedbackDrawable extends LayerDrawable {
+    private static final String LOG_TAG = TouchFeedbackDrawable.class.getSimpleName();
     private static final PorterDuffXfermode DST_IN = new PorterDuffXfermode(Mode.DST_IN);
 
     /** The maximum number of ripples supported. */
@@ -308,6 +310,11 @@
             mTouchedRipples = new SparseArray<Ripple>();
             mActiveRipples = new Ripple[MAX_RIPPLES];
         }
+        
+        if (mActiveRipplesCount >= MAX_RIPPLES) {
+            Log.e(LOG_TAG, "Max ripple count exceeded", new RuntimeException());
+            return;
+        }
 
         final Ripple ripple = mTouchedRipples.get(id);
         if (ripple == null) {
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index cf21834..9aa47a3 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -26,6 +26,7 @@
 #include "Debug.h"
 #include "DisplayListOp.h"
 #include "DisplayListLogBuffer.h"
+#include "utils/MathUtils.h"
 
 namespace android {
 namespace uirenderer {
@@ -217,7 +218,9 @@
         mat4 anim(*properties().getAnimationMatrix());
         matrix.multiply(anim);
     }
-    if (properties().hasTransformMatrix()) {
+
+    bool applyTranslationZ = true3dTransform && !MathUtils::isZero(properties().getTranslationZ());
+    if (properties().hasTransformMatrix() || applyTranslationZ) {
         if (properties().isTransformTranslateOnly()) {
             matrix.translate(properties().getTranslationX(), properties().getTranslationY(),
                     true3dTransform ? properties().getTranslationZ() : 0.0f);
@@ -391,7 +394,7 @@
         RenderNode* child = childOp->mDisplayList;
         float childZ = child->properties().getTranslationZ();
 
-        if (childZ != 0.0f) {
+        if (!MathUtils::isZero(childZ)) {
             zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
             childOp->mSkipInOrderDraw = true;
         } else if (!child->properties().getProjectBackwards()) {
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index 58ec32d..a922db8 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -26,16 +26,7 @@
 #include <SkPathOps.h>
 
 #include "Matrix.h"
-
-/**
- * Convenience value to check for float values that are close enough to zero to be considered
- * zero.
- */
-#define NONZERO_EPSILON .001f
-
-static inline bool is_zero(float value) {
-    return (value >= -NONZERO_EPSILON) && (value <= NONZERO_EPSILON);
-}
+#include "utils/MathUtils.h"
 
 namespace android {
 namespace uirenderer {
@@ -151,7 +142,7 @@
         }
         SkMatrix* transform = mComputedFields.mTransformMatrix;
         transform->reset();
-        if (is_zero(getRotationX()) && is_zero(getRotationY())) {
+        if (MathUtils::isZero(getRotationX()) && MathUtils::isZero(getRotationY())) {
             transform->setTranslate(getTranslationX(), getTranslationY());
             transform->preRotate(getRotation(), getPivotX(), getPivotY());
             transform->preScale(getScaleX(), getScaleY(), getPivotX(), getPivotY());
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
new file mode 100644
index 0000000..57ba8fa
--- /dev/null
+++ b/libs/hwui/utils/MathUtils.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef MATHUTILS_H
+#define MATHUTILS_H
+
+namespace android {
+namespace uirenderer {
+
+class MathUtils {
+private:
+    static const float gNonZeroEpsilon = 0.001f;
+public:
+    /**
+     * Check for floats that are close enough to zero.
+     */
+    inline static bool isZero(float value) {
+        return (value >= -gNonZeroEpsilon) && (value <= gNonZeroEpsilon);
+    }
+}; // class MathUtils
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* RENDERNODE_H */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 081e8de..ffdb620 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -157,24 +157,6 @@
     private static final int KEYGUARD_DONE_DRAWING_TIMEOUT_MS = 2000;
 
     /**
-     * Allow the user to expand the status bar when the keyguard is engaged
-     * (without a pattern or password).
-     */
-    private static final boolean ENABLE_INSECURE_STATUS_BAR_EXPAND = true;
-
-    /**
-     * Allow the user to expand the status bar when a SECURE keyguard is engaged
-     * and {@link android.provider.Settings.Global#LOCK_SCREEN_SHOW_NOTIFICATIONS} is set
-     * (private notifications will be masked).
-     */
-    private static final boolean ENABLE_SECURE_STATUS_BAR_EXPAND = true;
-
-    /**
-     * Default value of {@link android.provider.Settings.Global#LOCK_SCREEN_SHOW_NOTIFICATIONS}.
-     */
-    private static final boolean ALLOW_NOTIFICATIONS_DEFAULT = false;
-
-    /**
      * Secure setting whether analytics are collected on the keyguard.
      */
     private static final String KEYGUARD_ANALYTICS_SETTING = "keyguard_analytics";
@@ -277,11 +259,6 @@
     private int mLockSoundStreamId;
 
     /**
-     * Tracks value of {@link android.provider.Settings.Global#LOCK_SCREEN_SHOW_NOTIFICATIONS}.
-     */
-    private boolean mAllowNotificationsWhenSecure;
-
-    /**
      * The volume applied to the lock/unlock sounds.
      */
     private float mLockSoundVolume;
@@ -895,13 +872,6 @@
             return;
         }
 
-        // note whether notification access should be allowed
-        mAllowNotificationsWhenSecure = ENABLE_SECURE_STATUS_BAR_EXPAND
-                && 0 != Settings.Global.getInt(
-                        mContext.getContentResolver(),
-                        Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS,
-                        ALLOW_NOTIFICATIONS_DEFAULT ? 1 : 0);
-
         // if the keyguard is already showing, don't bother
         if (mStatusBarKeyguardViewManager.isShowing()) {
             if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
@@ -1271,11 +1241,6 @@
                 // (like recents). Temporary enable/disable (e.g. the "back" button) are
                 // done in KeyguardHostView.
                 flags |= StatusBarManager.DISABLE_RECENT;
-                if (isSecure()) {
-                    // showing secure lockscreen; disable ticker and switch private notifications
-                    // to show their public versions, if available.
-                    flags |= StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS;
-                }
                 if (!isAssistantAvailable()) {
                     flags |= StatusBarManager.DISABLE_SEARCH;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java b/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java
index 87ebcc1..d67e7cb 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java
@@ -38,6 +38,7 @@
 import android.widget.TextView;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * A quick and dirty view to show a user switcher.
@@ -118,7 +119,12 @@
 
     public void refreshUsers() {
         mUserInfo.clear();
-        mUserInfo.addAll(mUserManager.getUsers(true));
+        List<UserInfo> users = mUserManager.getUsers(true);
+        for (UserInfo user : users) {
+            if (!user.isManagedProfile()) {
+                mUserInfo.add(user);
+            }
+        }
         mAdapter.notifyDataSetChanged();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 56f83df..bb481ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -172,7 +172,7 @@
         int oldHeight = lp.height;
         lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
         setLayoutParams(lp);
-        measure(View.MeasureSpec.makeMeasureSpec(getMeasuredWidth(), View.MeasureSpec.EXACTLY),
+        measure(View.MeasureSpec.makeMeasureSpec(getWidth(), View.MeasureSpec.EXACTLY),
                 View.MeasureSpec.makeMeasureSpec(mRowMaxHeight, View.MeasureSpec.AT_MOST));
         lp.height = oldHeight;
         setLayoutParams(lp);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index cf31b44..1ffb4ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -143,6 +143,10 @@
         return false;
     }
 
+    public boolean isSecure() {
+        return mKeyguardView == null || mKeyguardView.getSecurityMode() != SecurityMode.None;
+    }
+
     public boolean onMenuPressed() {
         ensureView();
         if (mKeyguardView.handleMenuKey()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 2d96c5e..7f1ddaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -25,7 +25,6 @@
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
-import static com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -77,7 +76,6 @@
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewPropertyAnimator;
-import android.view.ViewStub;
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.view.animation.AccelerateInterpolator;
@@ -148,6 +146,11 @@
     private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService
     private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
 
+    /**
+     * Default value of {@link android.provider.Settings.Global#LOCK_SCREEN_SHOW_NOTIFICATIONS}.
+     */
+    private static final boolean ALLOW_NOTIFICATIONS_DEFAULT = false;
+
     private static final int STATUS_OR_NAV_TRANSIENT =
             View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
     private static final long AUTOHIDE_TIMEOUT_MS = 3000;
@@ -781,10 +784,11 @@
             if (!qsTap && !inButton) {
                 mSettingsTracker.computeCurrentVelocity(1000);
                 final float vy = mSettingsTracker.getYVelocity();
+                final boolean animate = true;
                 if (dy <= slop || vy <= 0) {
-                    flipToNotifications();
+                    flipToNotifications(animate);
                 } else {
-                    flipToSettings();
+                    flipToSettings(animate);
                 }
             }
             mSettingsTracker.recycle();
@@ -1452,8 +1456,6 @@
         flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " ");
         flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts");
         flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " ");
-        flagdbg.append(((state & StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS) != 0) ? "PRIVATE" : "private");
-        flagdbg.append(((diff  & StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS) != 0) ? "* " : " ");
         flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
         flagdbg.append(((diff  & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
         flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
@@ -1538,15 +1540,6 @@
                     .setDuration(175)
                     .start();
             }
-        } else if ((diff & StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS) != 0) {
-            if ((state & StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS) != 0) {
-                // we are outside a secure keyguard, so we need to switch to "public" mode
-                setLockscreenPublicMode(true);
-            } else {
-                // user has authenticated the device; full notifications may be shown
-                setLockscreenPublicMode(false);
-            }
-            updateNotificationIcons();
         }
     }
 
@@ -1669,7 +1662,7 @@
             mStatusBarWindow.cancelExpandHelper();
             mStatusBarView.collapseAllPanels(true);
             if (isFlippedToSettings()) {
-                flipToNotifications();
+                flipToNotifications(true /*animate*/);
             }
         }
     }
@@ -1729,36 +1722,49 @@
 
         mNotificationPanel.expand();
         if (mStackScroller.getVisibility() != View.VISIBLE) {
-            flipToNotifications();
+            flipToNotifications(true /*animate*/);
         }
 
         if (false) postStartTracing();
     }
 
-    public void flipToNotifications() {
-        if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
-        if (mScrollViewAnim != null) mScrollViewAnim.cancel();
+    private static void cancelAnim(Animator anim) {
+        if (anim != null) {
+            anim.cancel();
+        }
+    }
+
+    public void flipToNotifications(boolean animate) {
+        cancelAnim(mFlipSettingsViewAnim);
+        cancelAnim(mScrollViewAnim);
+        cancelAnim(mClearButtonAnim);
         mHeaderFlipper.cancel();
         mKeyguardFlipper.cancel();
-        if (mClearButtonAnim != null) mClearButtonAnim.cancel();
         mStackScroller.setVisibility(View.VISIBLE);
         final int h = mNotificationPanel.getMeasuredHeight();
-        final float settingsY = mSettingsTracker != null ? mFlipSettingsView.getTranslationY() : 0;
-        final float scrollerY = mSettingsTracker != null ? mStackScroller.getTranslationY() : h;
-        mScrollViewAnim = start(
-                interpolator(mDecelerateInterpolator,
-                    ObjectAnimator.ofFloat(mStackScroller, View.TRANSLATION_Y, scrollerY, 0)
-                        .setDuration(FLIP_DURATION)
-                    ));
-        mFlipSettingsViewAnim = start(
-            setVisibilityWhenDone(
-                interpolator(mDecelerateInterpolator,
-                        ObjectAnimator.ofFloat(mFlipSettingsView, View.TRANSLATION_Y, settingsY, -h)
-                        )
-                    .setDuration(FLIP_DURATION),
-                mFlipSettingsView, View.INVISIBLE));
-        mHeaderFlipper.flipToNotifications();
-        mKeyguardFlipper.flipToNotifications();
+        if (animate) {
+            final float settingsY =
+                    mSettingsTracker != null ? mFlipSettingsView.getTranslationY() : 0;
+            final float scrollerY = mSettingsTracker != null ? mStackScroller.getTranslationY() : h;
+            mScrollViewAnim = start(
+                    interpolator(mDecelerateInterpolator,
+                        ObjectAnimator.ofFloat(mStackScroller, View.TRANSLATION_Y, scrollerY, 0)
+                            .setDuration(FLIP_DURATION)
+                        ));
+            mFlipSettingsViewAnim = start(
+                setVisibilityWhenDone(
+                    interpolator(mDecelerateInterpolator,
+                            ObjectAnimator.ofFloat(
+                                    mFlipSettingsView, View.TRANSLATION_Y, settingsY, -h))
+                        .setDuration(FLIP_DURATION),
+                    mFlipSettingsView, View.INVISIBLE));
+        } else {
+            mStackScroller.setTranslationY(0);
+            mFlipSettingsView.setTranslationY(-h);
+            mFlipSettingsView.setVisibility(View.INVISIBLE);
+        }
+        mHeaderFlipper.flipToNotifications(animate);
+        mKeyguardFlipper.flipToNotifications(animate);
         mClearButton.setVisibility(View.VISIBLE);
         mClearButton.setAlpha(0f);
         setAreThereNotifications(); // this will show/hide the button as necessary
@@ -1766,7 +1772,7 @@
             public void run() {
                 updateCarrierLabelVisibility(false);
             }
-        }, FLIP_DURATION - 150);
+        }, animate ? FLIP_DURATION - 150 : 0);
         if (mOnFlipRunnable != null) {
             mOnFlipRunnable.run();
         }
@@ -1785,7 +1791,7 @@
         mNotificationPanel.expand();
         if (mFlipSettingsView.getVisibility() != View.VISIBLE
                 || mFlipSettingsView.getTranslationY() < 0) {
-            flipToSettings();
+            flipToSettings(true /*animate*/);
         }
 
         if (false) postStartTracing();
@@ -1798,58 +1804,63 @@
         return false;
     }
 
-    public void flipToSettings() {
+    public void flipToSettings(boolean animate) {
         // Settings are not available in setup
         if (!mUserSetup) return;
 
-        if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
-        if (mScrollViewAnim != null) mScrollViewAnim.cancel();
+        cancelAnim(mFlipSettingsViewAnim);
+        cancelAnim(mScrollViewAnim);
         mHeaderFlipper.cancel();
         mKeyguardFlipper.cancel();
-        if (mClearButtonAnim != null) mClearButtonAnim.cancel();
+        cancelAnim(mClearButtonAnim);
 
         mFlipSettingsView.setVisibility(View.VISIBLE);
         final int h = mNotificationPanel.getMeasuredHeight();
-        final float settingsY = mSettingsTracker != null ? mFlipSettingsView.getTranslationY() : -h;
-        final float scrollerY = mSettingsTracker != null ? mStackScroller.getTranslationY() : 0;
-        mFlipSettingsViewAnim = start(
-                startDelay(0,
+        if (animate) {
+            final float settingsY
+                    = mSettingsTracker != null ? mFlipSettingsView.getTranslationY() : -h;
+            final float scrollerY = mSettingsTracker != null ? mStackScroller.getTranslationY() : 0;
+            mFlipSettingsViewAnim = start(
+                    startDelay(0,
+                        interpolator(mDecelerateInterpolator,
+                            ObjectAnimator.ofFloat(mFlipSettingsView, View.TRANSLATION_Y,
+                                    settingsY, 0f)
+                                .setDuration(FLIP_DURATION)
+                            )));
+            mScrollViewAnim = start(
+                setVisibilityWhenDone(
                     interpolator(mDecelerateInterpolator,
-                        ObjectAnimator.ofFloat(mFlipSettingsView, View.TRANSLATION_Y, settingsY, 0f)
-                            .setDuration(FLIP_DURATION)
-                        )));
-        mScrollViewAnim = start(
-            setVisibilityWhenDone(
-                interpolator(mDecelerateInterpolator,
-                        ObjectAnimator.ofFloat(mStackScroller, View.TRANSLATION_Y, scrollerY, h)
-                        )
+                            ObjectAnimator.ofFloat(mStackScroller, View.TRANSLATION_Y, scrollerY, h)
+                            )
+                        .setDuration(FLIP_DURATION),
+                        mStackScroller, View.INVISIBLE));
+        } else {
+            mFlipSettingsView.setTranslationY(0);
+            mStackScroller.setTranslationY(h);
+            mStackScroller.setVisibility(View.INVISIBLE);
+        }
+        mHeaderFlipper.flipToSettings(animate);
+        mKeyguardFlipper.flipToSettings(animate);
+        if (animate) {
+            mClearButtonAnim = start(
+                setVisibilityWhenDone(
+                    ObjectAnimator.ofFloat(mClearButton, View.ALPHA, 0f)
                     .setDuration(FLIP_DURATION),
-                    mStackScroller, View.INVISIBLE));
-        mHeaderFlipper.flipToSettings();
-        mKeyguardFlipper.flipToSettings();
-        mClearButtonAnim = start(
-            setVisibilityWhenDone(
-                ObjectAnimator.ofFloat(mClearButton, View.ALPHA, 0f)
-                .setDuration(FLIP_DURATION),
-                mClearButton, View.INVISIBLE));
+                    mClearButton, View.INVISIBLE));
+        } else {
+            mClearButton.setAlpha(0);
+            mClearButton.setVisibility(View.INVISIBLE);
+        }
         mNotificationPanel.postDelayed(new Runnable() {
             public void run() {
                 updateCarrierLabelVisibility(false);
             }
-        }, FLIP_DURATION - 150);
+        }, animate ? FLIP_DURATION - 150 : 0);
         if (mOnFlipRunnable != null) {
             mOnFlipRunnable.run();
         }
     }
 
-    public void flipPanels() {
-        if (mFlipSettingsView.getVisibility() != View.VISIBLE) {
-            flipToSettings();
-        } else {
-            flipToNotifications();
-        }
-    }
-
     public void animateCollapseQuickSettings() {
         mStatusBarView.collapseAllPanels(true);
     }
@@ -2910,31 +2921,44 @@
 
     public void showKeyguard() {
         mOnKeyguard = true;
+        updateKeyguardState();
         instantExpandNotificationsPanel();
-        if (isFlippedToSettings()) {
-            flipToNotifications();
-        }
-        mKeyguardStatusView.setVisibility(View.VISIBLE);
-        mKeyguardBottomArea.setVisibility(View.VISIBLE);
-        mNotificationPanelHeader.setVisibility(View.GONE);
-
-        mKeyguardFlipper.setVisibility(View.VISIBLE);
-        mSettingsContainer.setKeyguardShowing(true);
-        updateRowStates();
-        checkBarModes();
     }
 
     public void hideKeyguard() {
         mOnKeyguard = false;
-        mKeyguardStatusView.setVisibility(View.GONE);
-        mKeyguardBottomArea.setVisibility(View.GONE);
-        mNotificationPanelHeader.setVisibility(View.VISIBLE);
-
-        mKeyguardFlipper.setVisibility(View.GONE);
-        mSettingsContainer.setKeyguardShowing(false);
-        updateRowStates();
+        updateKeyguardState();
         instantCollapseNotificationPanel();
+    }
+
+    private void updatePublicMode() {
+        setLockscreenPublicMode(mOnKeyguard && mStatusBarKeyguardViewManager.isSecure());
+    }
+
+    private void updateKeyguardState() {
+        if (mOnKeyguard) {
+            if (isFlippedToSettings()) {
+                flipToNotifications(false /*animate*/);
+            }
+            mKeyguardStatusView.setVisibility(View.VISIBLE);
+            mKeyguardBottomArea.setVisibility(View.VISIBLE);
+            mNotificationPanelHeader.setVisibility(View.GONE);
+
+            mKeyguardFlipper.setVisibility(View.VISIBLE);
+            mSettingsContainer.setKeyguardShowing(true);
+        } else {
+            mKeyguardStatusView.setVisibility(View.GONE);
+            mKeyguardBottomArea.setVisibility(View.GONE);
+            mNotificationPanelHeader.setVisibility(View.VISIBLE);
+
+            mKeyguardFlipper.setVisibility(View.GONE);
+            mSettingsContainer.setKeyguardShowing(false);
+        }
+
+        updatePublicMode();
+        updateRowStates();
         checkBarModes();
+        updateNotificationIcons();
     }
 
     public void userActivity() {
@@ -3075,36 +3099,48 @@
             }
         }
 
-        public void flipToSettings() {
-            mSettingsButtonAnim = start(
-                setVisibilityWhenDone(
-                    ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f)
-                        .setDuration(FLIP_DURATION_OUT),
-                    mStackScroller, View.INVISIBLE));
+        public void flipToSettings(boolean animate) {
             mNotificationButton.setVisibility(View.VISIBLE);
-            mNotificationButtonAnim = start(
-                startDelay(FLIP_DURATION_OUT,
-                    ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f)
-                        .setDuration(FLIP_DURATION_IN)));
+            if (animate) {
+                mSettingsButtonAnim = start(
+                    setVisibilityWhenDone(
+                        ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f)
+                            .setDuration(FLIP_DURATION_OUT),
+                        mStackScroller, View.INVISIBLE));
+                mNotificationButtonAnim = start(
+                    startDelay(FLIP_DURATION_OUT,
+                        ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f)
+                            .setDuration(FLIP_DURATION_IN)));
+            } else {
+                mSettingsButton.setAlpha(0f);
+                mSettingsButton.setVisibility(View.INVISIBLE);
+                mNotificationButton.setAlpha(1f);
+            }
         }
 
-        public void flipToNotifications() {
-            mNotificationButtonAnim = start(
-                setVisibilityWhenDone(
-                    ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 0f)
-                        .setDuration(FLIP_DURATION_OUT),
-                    mNotificationButton, View.INVISIBLE));
-
+        public void flipToNotifications(boolean animate) {
             mSettingsButton.setVisibility(View.VISIBLE);
-            mSettingsButtonAnim = start(
-                startDelay(FLIP_DURATION_OUT,
-                    ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 1f)
-                        .setDuration(FLIP_DURATION_IN)));
+            if (animate) {
+                mNotificationButtonAnim = start(
+                    setVisibilityWhenDone(
+                        ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 0f)
+                            .setDuration(FLIP_DURATION_OUT),
+                        mNotificationButton, View.INVISIBLE));
+
+                mSettingsButtonAnim = start(
+                    startDelay(FLIP_DURATION_OUT,
+                        ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 1f)
+                            .setDuration(FLIP_DURATION_IN)));
+            } else {
+                mNotificationButton.setVisibility(View.INVISIBLE);
+                mNotificationButton.setAlpha(0f);
+                mSettingsButton.setAlpha(1f);
+            }
         }
 
         public void cancel() {
-            if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel();
-            if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
+            cancelAnim(mSettingsButtonAnim);
+            cancelAnim(mNotificationButtonAnim);
         }
 
         public void setVisibility(int vis) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index b3fba76..f3ebf1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -280,6 +280,7 @@
         addUserTiles(mContainerView, inflater);
         addSystemTiles(mContainerView, inflater);
         addTemporaryTiles(mContainerView, inflater);
+        addAccessibilityTiles(mContainerView);
 
         queryForUserInformation();
         queryForSslCaCerts();
@@ -311,6 +312,34 @@
         collapsePanels();
     }
 
+    private void addAccessibilityTiles(ViewGroup parent) {
+        if (!DEBUG_GONE_TILES && !SHOW_ACCESSIBILITY_TILES) return;
+
+        // Color inversion tile
+        final SystemSettingTile inversionTile = new SystemSettingTile(mContext);
+        inversionTile.setUri(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
+                SystemSettingTile.TYPE_SECURE);
+        inversionTile.setFragment("Settings$AccessibilityInversionSettingsActivity");
+        mModel.addInversionTile(inversionTile, inversionTile.getRefreshCallback());
+        parent.addView(inversionTile);
+
+        // Contrast enhancement tile
+        final SystemSettingTile contrastTile = new SystemSettingTile(mContext);
+        contrastTile.setUri(Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED,
+                SystemSettingTile.TYPE_SECURE);
+        contrastTile.setFragment("Settings$AccessibilityContrastSettingsActivity");
+        mModel.addContrastTile(contrastTile, contrastTile.getRefreshCallback());
+        parent.addView(contrastTile);
+
+        // Color space adjustment tile
+        final SystemSettingTile colorSpaceTile = new SystemSettingTile(mContext);
+        colorSpaceTile.setUri(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
+                SystemSettingTile.TYPE_SECURE);
+        colorSpaceTile.setFragment("Settings$AccessibilityDaltonizerSettingsActivity");
+        mModel.addColorSpaceTile(colorSpaceTile, colorSpaceTile.getRefreshCallback());
+        parent.addView(colorSpaceTile);
+    }
+
     private void addUserTiles(final ViewGroup parent, final LayoutInflater inflater) {
         QuickSettingsTileView userTile = (QuickSettingsTileView)
                 inflater.inflate(R.layout.quick_settings_tile, parent, false);
@@ -384,35 +413,6 @@
                 new QuickSettingsModel.BasicRefreshCallback(settingsTile));
         parent.addView(settingsTile);
         mDynamicSpannedTiles.add(settingsTile);
-
-        if (SHOW_ACCESSIBILITY_TILES) {
-            // Color inversion tile
-            final SystemSettingTile inversionTile = new SystemSettingTile(mContext);
-            inversionTile.setUri(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
-                    SystemSettingTile.TYPE_SECURE);
-            inversionTile.setFragment("Settings$AccessibilityInversionSettingsActivity");
-            mModel.addInversionTile(inversionTile, inversionTile.getRefreshCallback());
-            parent.addView(inversionTile);
-            mDynamicSpannedTiles.add(inversionTile);
-
-            // Contrast enhancement tile
-            final SystemSettingTile contrastTile = new SystemSettingTile(mContext);
-            contrastTile.setUri(Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED,
-                    SystemSettingTile.TYPE_SECURE);
-            contrastTile.setFragment("Settings$AccessibilityContrastSettingsActivity");
-            mModel.addContrastTile(contrastTile, contrastTile.getRefreshCallback());
-            parent.addView(contrastTile);
-            mDynamicSpannedTiles.add(contrastTile);
-
-            // Color space adjustment tile
-            final SystemSettingTile colorSpaceTile = new SystemSettingTile(mContext);
-            colorSpaceTile.setUri(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
-                    SystemSettingTile.TYPE_SECURE);
-            colorSpaceTile.setFragment("Settings$AccessibilityDaltonizerSettingsActivity");
-            mModel.addColorSpaceTile(colorSpaceTile, colorSpaceTile.getRefreshCallback());
-            parent.addView(colorSpaceTile);
-            mDynamicSpannedTiles.add(colorSpaceTile);
-        }
     }
 
     private void addSystemTiles(ViewGroup parent, LayoutInflater inflater) {
@@ -574,49 +574,6 @@
         });
         parent.addView(batteryTile);
 
-        // Airplane Mode
-        final QuickSettingsBasicTile airplaneTile
-                = new QuickSettingsBasicTile(mContext);
-        mModel.addAirplaneModeTile(airplaneTile, new QuickSettingsModel.RefreshCallback() {
-            @Override
-            public void refreshView(QuickSettingsTileView unused, State state) {
-                airplaneTile.setImageResource(state.iconId);
-
-                String airplaneState = mContext.getString(
-                        (state.enabled) ? R.string.accessibility_desc_on
-                                : R.string.accessibility_desc_off);
-                airplaneTile.setContentDescription(
-                        mContext.getString(R.string.accessibility_quick_settings_airplane, airplaneState));
-                airplaneTile.setText(state.label);
-            }
-        });
-        parent.addView(airplaneTile);
-
-        // Zen Mode
-        final QuickSettingsBasicTile zenModeTile = new QuickSettingsBasicTile(mContext);
-        zenModeTile.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                showZenModeDialog();
-            }
-        });
-        mModel.addZenModeTile(zenModeTile, new QuickSettingsModel.RefreshCallback() {
-            @Override
-            public void refreshView(QuickSettingsTileView unused, State state) {
-                zenModeTile.setImageResource(state.iconId);
-                // TODO cut new assets
-                zenModeTile.getImageView().setAlpha(state.enabled ? 1 : .2f);
-                zenModeTile.getImageView().setScaleX(1.5f);
-                zenModeTile.getImageView().setScaleY(1.5f);
-                // for landscape version
-                zenModeTile.getTextView().setMaxLines(2);
-                zenModeTile.getTextView().setEllipsize(TruncateAt.END);
-                // TODO content description
-                zenModeTile.setText(state.label);
-            }
-        });
-        parent.addView(zenModeTile);
-
         // Bluetooth
         if (mModel.deviceSupportsBluetooth()
                 || DEBUG_GONE_TILES) {
@@ -710,6 +667,50 @@
             }
         });
         parent.addView(locationTile);
+
+        // Airplane Mode
+        final QuickSettingsBasicTile airplaneTile
+                = new QuickSettingsBasicTile(mContext);
+        mModel.addAirplaneModeTile(airplaneTile, new QuickSettingsModel.RefreshCallback() {
+            @Override
+            public void refreshView(QuickSettingsTileView unused, State state) {
+                airplaneTile.setImageResource(state.iconId);
+
+                String airplaneState = mContext.getString(
+                        (state.enabled) ? R.string.accessibility_desc_on
+                                : R.string.accessibility_desc_off);
+                airplaneTile.setContentDescription(
+                        mContext.getString(R.string.accessibility_quick_settings_airplane,
+                                airplaneState));
+                airplaneTile.setText(state.label);
+            }
+        });
+        parent.addView(airplaneTile);
+
+        // Zen Mode
+        final QuickSettingsBasicTile zenModeTile = new QuickSettingsBasicTile(mContext);
+        zenModeTile.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showZenModeDialog();
+            }
+        });
+        mModel.addZenModeTile(zenModeTile, new QuickSettingsModel.RefreshCallback() {
+            @Override
+            public void refreshView(QuickSettingsTileView unused, State state) {
+                zenModeTile.setImageResource(state.iconId);
+                // TODO cut new assets
+                zenModeTile.getImageView().setAlpha(state.enabled ? 1 : .2f);
+                zenModeTile.getImageView().setScaleX(1.5f);
+                zenModeTile.getImageView().setScaleY(1.5f);
+                // for landscape version
+                zenModeTile.getTextView().setMaxLines(2);
+                zenModeTile.getTextView().setEllipsize(TruncateAt.END);
+                // TODO content description
+                zenModeTile.setText(state.label);
+            }
+        });
+        parent.addView(zenModeTile);
     }
 
     private void addTemporaryTiles(final ViewGroup parent, final LayoutInflater inflater) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 501d3f2..c2595cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -179,6 +179,10 @@
         }
     }
 
+    public boolean isSecure() {
+        return mBouncer.isSecure();
+    }
+
     /**
      * @return Whether the keyguard is showing
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index a7363f4..d9e7f66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -533,7 +533,23 @@
             } else {
 
                 // We are expanding the shade, expand it to its full height.
-                mFirstChildMaxHeight = getMaxAllowedChildHeight(mFirstChildWhileExpanding);
+                if (mFirstChildWhileExpanding.getWidth() == 0) {
+
+                    // This child was not layouted yet, wait for a layout pass
+                    mFirstChildWhileExpanding
+                            .addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+                                @Override
+                                public void onLayoutChange(View v, int left, int top, int right,
+                                        int bottom, int oldLeft, int oldTop, int oldRight,
+                                        int oldBottom) {
+                                    mFirstChildMaxHeight = getMaxAllowedChildHeight(
+                                            mFirstChildWhileExpanding);
+                                    mFirstChildWhileExpanding.removeOnLayoutChangeListener(this);
+                                }
+                            });
+                } else {
+                    mFirstChildMaxHeight = getMaxAllowedChildHeight(mFirstChildWhileExpanding);
+                }
             }
         } else {
             mFirstChildMaxHeight = 0;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 51296c1..8fa076b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3453,11 +3453,14 @@
      * @param token The Binder token referencing the Activity we want to finish.
      * @param resultCode Result code, if any, from this Activity.
      * @param resultData Result data (Intent), if any, from this Activity.
+     * @param finishTask Whether to finish the task associated with this Activity.  Only applies to
+     *            the root Activity in the task.
      *
      * @return Returns true if the activity successfully finished, or false if it is still running.
      */
     @Override
-    public final boolean finishActivity(IBinder token, int resultCode, Intent resultData) {
+    public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
+            boolean finishTask) {
         // Refuse possible leaked file descriptors
         if (resultData != null && resultData.hasFileDescriptors() == true) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -3468,6 +3471,9 @@
             if (r == null) {
                 return true;
             }
+            // Keep track of the root activity of the task before we finish it
+            TaskRecord tr = r.task;
+            ActivityRecord rootR = tr.getRootActivity();
             if (mController != null) {
                 // Find the first activity that is not finishing.
                 ActivityRecord next = r.task.stack.topRunningActivityLocked(token, 0);
@@ -3487,10 +3493,21 @@
                 }
             }
             final long origId = Binder.clearCallingIdentity();
-            boolean res = r.task.stack.requestFinishActivityLocked(token, resultCode,
-                    resultData, "app-request", true);
-            Binder.restoreCallingIdentity(origId);
-            return res;
+            try {
+                boolean res;
+                if (finishTask && r == rootR) {
+                    // If requested, remove the task that is associated to this activity only if it
+                    // was the root activity in the task.  The result code and data is ignored because
+                    // we don't support returning them across task boundaries.
+                    res = removeTaskByIdLocked(tr.taskId, 0);
+                } else {
+                    res = tr.stack.requestFinishActivityLocked(token, resultCode,
+                            resultData, "app-request", true);
+                }
+                return res;
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
         }
     }
 
@@ -7144,6 +7161,24 @@
         }
     }
 
+    /**
+     * Removes the task with the specified task id.
+     *
+     * @param taskId Identifier of the task to be removed.
+     * @param flags Additional operational flags.  May be 0 or
+     * {@link ActivityManager#REMOVE_TASK_KILL_PROCESS}.
+     * @return Returns true if the given task was found and removed.
+     */
+    private boolean removeTaskByIdLocked(int taskId, int flags) {
+        TaskRecord tr = recentTaskForIdLocked(taskId);
+        if (tr != null) {
+            tr.removeTaskActivitiesLocked(-1, false);
+            cleanUpRemovedTaskLocked(tr, flags);
+            return true;
+        }
+        return false;
+    }
+
     @Override
     public boolean removeTask(int taskId, int flags) {
         synchronized (this) {
@@ -7151,29 +7186,11 @@
                     "removeTask()");
             long ident = Binder.clearCallingIdentity();
             try {
-                TaskRecord tr = recentTaskForIdLocked(taskId);
-                if (tr != null) {
-                    ActivityRecord r = tr.removeTaskActivitiesLocked(-1, false);
-                    if (r != null) {
-                        cleanUpRemovedTaskLocked(tr, flags);
-                        return true;
-                    }
-                    if (tr.mActivities.size() == 0) {
-                        // Caller is just removing a recent task that is
-                        // not actively running.  That is easy!
-                        cleanUpRemovedTaskLocked(tr, flags);
-                        return true;
-                    }
-                    Slog.w(TAG, "removeTask: task " + taskId
-                            + " does not have activities to remove, "
-                            + " but numActivities=" + tr.numActivities
-                            + ": " + tr);
-                }
+                return removeTaskByIdLocked(taskId, flags);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
         }
-        return false;
     }
     
     /**
diff --git a/services/core/java/com/android/server/am/NativeCrashListener.java b/services/core/java/com/android/server/am/NativeCrashListener.java
index b12843b..443218c 100644
--- a/services/core/java/com/android/server/am/NativeCrashListener.java
+++ b/services/core/java/com/android/server/am/NativeCrashListener.java
@@ -29,6 +29,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
 import java.net.InetSocketAddress;
 import java.net.InetUnixAddress;
 
@@ -178,7 +179,7 @@
     }
 
     static int readExactly(FileDescriptor fd, byte[] buffer, int offset, int numBytes)
-            throws ErrnoException {
+            throws ErrnoException, InterruptedIOException {
         int totalRead = 0;
         while (numBytes > 0) {
             int n = Libcore.os.read(fd, buffer, offset + totalRead, numBytes);
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 3a43521..80a219dd 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -139,6 +139,18 @@
         }
     }
 
+    /** Returns the first non-finishing activity from the root. */
+    ActivityRecord getRootActivity() {
+        for (int i = 0; i < mActivities.size(); i++) {
+            final ActivityRecord r = mActivities.get(i);
+            if (r.finishing) {
+                continue;
+            }
+            return r;
+        }
+        return null;
+    }
+
     ActivityRecord getTopActivity() {
         for (int i = mActivities.size() - 1; i >= 0; --i) {
             final ActivityRecord r = mActivities.get(i);
@@ -305,7 +317,7 @@
     }
 
     public ActivityManager.TaskThumbnails getTaskThumbnailsLocked() {
-        TaskAccessInfo info = getTaskAccessInfoLocked(true);
+        TaskAccessInfo info = getTaskAccessInfoLocked();
         final ActivityRecord resumedActivity = stack.mResumedActivity;
         if (resumedActivity != null && resumedActivity.thumbHolder == this) {
             info.mainThumbnail = stack.screenshotActivities(resumedActivity);
@@ -325,7 +337,7 @@
         }
         // Return the information about the task, to figure out the top
         // thumbnail to return.
-        TaskAccessInfo info = getTaskAccessInfoLocked(true);
+        TaskAccessInfo info = getTaskAccessInfoLocked();
         if (info.numSubThumbbails <= 0) {
             return info.mainThumbnail != null ? info.mainThumbnail : lastThumbnail;
         }
@@ -334,7 +346,7 @@
 
     public ActivityRecord removeTaskActivitiesLocked(int subTaskIndex,
             boolean taskRequired) {
-        TaskAccessInfo info = getTaskAccessInfoLocked(false);
+        TaskAccessInfo info = getTaskAccessInfoLocked();
         if (info.root == null) {
             if (taskRequired) {
                 Slog.w(TAG, "removeTaskLocked: unknown taskId " + taskId);
@@ -369,7 +381,7 @@
         return mTaskType == ActivityRecord.APPLICATION_ACTIVITY_TYPE;
     }
 
-    public TaskAccessInfo getTaskAccessInfoLocked(boolean inclThumbs) {
+    public TaskAccessInfo getTaskAccessInfoLocked() {
         final TaskAccessInfo thumbs = new TaskAccessInfo();
         // How many different sub-thumbnails?
         final int NA = mActivities.size();
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a6e83a7..53db9ef 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -22,6 +22,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.ActivityThread;
+import android.app.admin.DevicePolicyManager;
 import android.app.IStopUserCallback;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -437,7 +438,7 @@
 
     @Override
     public void setUserRestrictions(Bundle restrictions, int userId) {
-        checkManageUsersPermission("setUserRestrictions");
+        checkProfileOwnerOrManageUsersPermission("setUserRestrictions");
         if (restrictions == null) return;
 
         synchronized (mPackagesLock) {
@@ -463,16 +464,53 @@
      * @param message used as message if SecurityException is thrown
      * @throws SecurityException if the caller is not system or root
      */
-    private static final void checkManageUsersPermission(String message) {
+    private final void checkManageUsersPermission(String message) {
         final int uid = Binder.getCallingUid();
-        if (uid != Process.SYSTEM_UID && uid != 0
-                && ActivityManager.checkComponentPermission(
-                        android.Manifest.permission.MANAGE_USERS,
-                        uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
+
+        if (missingManageUsersPermission(uid)) {
             throw new SecurityException("You need MANAGE_USERS permission to: " + message);
         }
     }
 
+    /**
+     * Enforces that only the system UID, root's UID, apps that have the
+     * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS}
+     * permission, the profile owner, or the device owner can make certain calls to the
+     * UserManager.
+     *
+     * @param message used as message if SecurityException is thrown
+     * @throws SecurityException if the caller is not system, root, or device
+     * owner
+     */
+    private final void checkProfileOwnerOrManageUsersPermission(String message) {
+        final int uid = Binder.getCallingUid();
+        boolean isProfileOwner = false;
+        if (mContext != null && mContext.getPackageManager() != null) {
+            String[] pkgs = mContext.getPackageManager().getPackagesForUid(uid);
+            DevicePolicyManager dpm =
+                    (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+            if (dpm != null) {
+                for (String pkg : pkgs) {
+                    if (dpm.isDeviceOwnerApp(pkg) || dpm.isProfileOwnerApp(pkg)) {
+                        isProfileOwner = true;
+                    }
+                }
+            }
+        }
+
+        if (missingManageUsersPermission(uid) && !isProfileOwner) {
+            throw new SecurityException(
+                    "You need MANAGE_USERS permission or device owner privileges to: " + message);
+        }
+    }
+
+    private boolean missingManageUsersPermission(int uid) {
+        return uid != Process.SYSTEM_UID && uid != 0
+                && ActivityManager.checkComponentPermission(
+                        android.Manifest.permission.MANAGE_USERS,
+                        uid, -1, true) != PackageManager.PERMISSION_GRANTED;
+    }
+
     private void writeBitmapLocked(UserInfo info, Bitmap bitmap) {
         try {
             File dir = new File(mUsersDir, Integer.toString(info.id));
@@ -1175,7 +1213,8 @@
     public Bundle getApplicationRestrictionsForUser(String packageName, int userId) {
         if (UserHandle.getCallingUserId() != userId
                 || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
-            checkManageUsersPermission("Only system can get restrictions for other users/apps");
+            checkProfileOwnerOrManageUsersPermission(
+                    "Only system or device owner can get restrictions for other users/apps");
         }
         synchronized (mPackagesLock) {
             // Read the restrictions from XML
@@ -1188,7 +1227,8 @@
             int userId) {
         if (UserHandle.getCallingUserId() != userId
                 || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
-            checkManageUsersPermission("Only system can set restrictions for other users/apps");
+            checkProfileOwnerOrManageUsersPermission(
+                    "Only system or device owner can set restrictions for other users/apps");
         }
         synchronized (mPackagesLock) {
             // Write the restrictions to XML
@@ -1290,7 +1330,8 @@
 
     @Override
     public void removeRestrictions() {
-        checkManageUsersPermission("Only system can remove restrictions");
+        checkProfileOwnerOrManageUsersPermission(
+                "Only system or device owner can remove restrictions");
         final int userHandle = UserHandle.getCallingUserId();
         removeRestrictionsForUser(userHandle, true);
     }
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
index cdbe200..af22f44 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -32,10 +32,6 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
-import android.view.InflateException;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
 
 import java.io.File;
 
@@ -155,6 +151,9 @@
     @Override
     public View inflate(int resource, ViewGroup root) {
         Context context = getContext();
+        if (context instanceof ContextThemeWrapper) {
+            context = ((ContextThemeWrapper) context).getBaseContext();
+        }
         if (context instanceof BridgeContext) {
             BridgeContext bridgeContext = (BridgeContext)context;
 
@@ -217,43 +216,16 @@
     }
 
     private void setupViewInContext(View view, AttributeSet attrs) {
-        if (getContext() instanceof BridgeContext) {
-            BridgeContext bc = (BridgeContext) getContext();
-            if (attrs instanceof BridgeXmlBlockParser) {
-                BridgeXmlBlockParser parser = (BridgeXmlBlockParser) attrs;
-
-                // get the view key
-                Object viewKey = parser.getViewCookie();
-
-                if (viewKey == null) {
-                    int currentDepth = parser.getDepth();
-
-                    // test whether we are in an included file or in a adapter binding view.
-                    BridgeXmlBlockParser previousParser = bc.getPreviousParser();
-                    if (previousParser != null) {
-                        // looks like we inside an embedded layout.
-                        // only apply the cookie of the calling node (<include>) if we are at the
-                        // top level of the embedded layout. If there is a merge tag, then
-                        // skip it and look for the 2nd level
-                        int testDepth = mIsInMerge ? 2 : 1;
-                        if (currentDepth == testDepth) {
-                            viewKey = previousParser.getViewCookie();
-                            // if we are in a merge, wrap the cookie in a MergeCookie.
-                            if (viewKey != null && mIsInMerge) {
-                                viewKey = new MergeCookie(viewKey);
-                            }
-                        }
-                    } else if (mResourceReference != null && currentDepth == 1) {
-                        // else if there's a resource reference, this means we are in an adapter
-                        // binding case. Set the resource ref as the view cookie only for the top
-                        // level view.
-                        viewKey = mResourceReference;
-                    }
-                }
-
-                if (viewKey != null) {
-                    bc.addViewKey(view, viewKey);
-                }
+        Context context = getContext();
+        if (context instanceof ContextThemeWrapper) {
+            context = ((ContextThemeWrapper) context).getBaseContext();
+        }
+        if (context instanceof BridgeContext) {
+            BridgeContext bc = (BridgeContext) context;
+            // get the view key
+            Object viewKey = getViewKeyFromParser(attrs, bc, mResourceReference, mIsInMerge);
+            if (viewKey != null) {
+                bc.addViewKey(view, viewKey);
             }
         }
     }
@@ -270,4 +242,44 @@
     public LayoutInflater cloneInContext(Context newContext) {
         return new BridgeInflater(this, newContext);
     }
+
+    /*package*/ static Object getViewKeyFromParser(AttributeSet attrs, BridgeContext bc,
+            ResourceReference resourceReference, boolean isInMerge) {
+
+        if (!(attrs instanceof BridgeXmlBlockParser)) {
+            return null;
+        }
+        BridgeXmlBlockParser parser = ((BridgeXmlBlockParser) attrs);
+
+        // get the view key
+        Object viewKey = parser.getViewCookie();
+
+        if (viewKey == null) {
+            int currentDepth = parser.getDepth();
+
+            // test whether we are in an included file or in a adapter binding view.
+            BridgeXmlBlockParser previousParser = bc.getPreviousParser();
+            if (previousParser != null) {
+                // looks like we are inside an embedded layout.
+                // only apply the cookie of the calling node (<include>) if we are at the
+                // top level of the embedded layout. If there is a merge tag, then
+                // skip it and look for the 2nd level
+                int testDepth = isInMerge ? 2 : 1;
+                if (currentDepth == testDepth) {
+                    viewKey = previousParser.getViewCookie();
+                    // if we are in a merge, wrap the cookie in a MergeCookie.
+                    if (viewKey != null && isInMerge) {
+                        viewKey = new MergeCookie(viewKey);
+                    }
+                }
+            } else if (resourceReference != null && currentDepth == 1) {
+                // else if there's a resource reference, this means we are in an adapter
+                // binding case. Set the resource ref as the view cookie only for the top
+                // level view.
+                viewKey = resourceReference;
+            }
+        }
+
+        return viewKey;
+    }
 }
diff --git a/tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java
new file mode 100644
index 0000000..22232f3
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 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.view;
+
+import android.content.Context;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.ViewInfo;
+import com.android.internal.view.menu.BridgeMenuItemImpl;
+import com.android.internal.view.menu.MenuView;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.util.AttributeSet;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link MenuInflater}
+ * <p/>
+ * Through the layoutlib_create tool, the original  methods of MenuInflater have been
+ * replaced by calls to methods of the same name in this delegate class.
+ * <p/>
+ * The main purpose of the class is to get the view key from the menu xml parser and add it to
+ * the menu item. The view key is used by the IDE to match the individual view elements to the
+ * corresponding xml tag in the menu/layout file.
+ * <p/>
+ * For Menus, the views may be reused and the {@link MenuItem} is a better object to hold the
+ * view key than the {@link MenuView.ItemView}. At the time of computation of the rest of {@link
+ * ViewInfo}, we check the corresponding view key in the menu item for the view and add it
+ */
+public class MenuInflater_Delegate {
+
+    @LayoutlibDelegate
+    /*package*/ static void emptyMethod(MenuInflater thisInflater, MenuItem menuItem,
+            AttributeSet attrs) {
+        if (menuItem instanceof BridgeMenuItemImpl) {
+            Context context = thisInflater.getContext();
+            if (context instanceof ContextThemeWrapper) {
+                context = ((ContextThemeWrapper) context).getBaseContext();
+            }
+            if (context instanceof BridgeContext) {
+                Object viewKey = BridgeInflater.getViewKeyFromParser(
+                        attrs, ((BridgeContext) context), null, false);
+                ((BridgeMenuItemImpl) menuItem).setViewCookie(viewKey);
+                return;
+            }
+        }
+        // This means that Bridge did not take over the instantiation of some object properly.
+        // This is most likely a bug in the LayoutLib code.
+        Bridge.getLog().warning(LayoutLog.TAG_BROKEN,
+                "Action Bar Menu rendering may be incorrect.", null);
+
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void emptyMethod(MenuInflater thisInflater, SubMenu subMenu,
+            AttributeSet parser) {
+        emptyMethod(thisInflater, subMenu.getItem(), parser);
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/com/android/internal/view/menu/BridgeMenuItemImpl.java b/tools/layoutlib/bridge/src/com/android/internal/view/menu/BridgeMenuItemImpl.java
new file mode 100644
index 0000000..4bef424
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/internal/view/menu/BridgeMenuItemImpl.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 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.internal.view.menu;
+
+/**
+ * An extension of the {@link MenuItemImpl} to store the view cookie also.
+ */
+public class BridgeMenuItemImpl extends MenuItemImpl {
+
+    /**
+     * An object returned by the IDE that helps mapping each View to the corresponding XML tag in
+     * the layout. For Menus, we store this cookie here and attach it to the corresponding view
+     * at the time of rendering.
+     */
+    private Object viewCookie;
+
+    /**
+     * Instantiates this menu item.
+     */
+    BridgeMenuItemImpl(MenuBuilder menu, int group, int id, int categoryOrder, int ordering,
+            CharSequence title, int showAsAction) {
+        super(menu, group, id, categoryOrder, ordering, title, showAsAction);
+    }
+
+
+    public Object getViewCookie() {
+        return viewCookie;
+    }
+
+    public void setViewCookie(Object viewCookie) {
+        this.viewCookie = viewCookie;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/internal/view/menu/MenuBuilder_Delegate.java b/tools/layoutlib/bridge/src/com/android/internal/view/menu/MenuBuilder_Delegate.java
new file mode 100644
index 0000000..505fb81
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/internal/view/menu/MenuBuilder_Delegate.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 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.internal.view.menu;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link MenuBuilder}
+ * <p/>
+ * Through the layoutlib_create tool, the original  methods of {@code MenuBuilder} have been
+ * replaced by calls to methods of the same name in this delegate class.
+ */
+public class MenuBuilder_Delegate {
+    /**
+     * The method overrides the instantiation of the {@link MenuItemImpl} with an instance of
+     * {@link BridgeMenuItemImpl} so that view cookies may be stored.
+     */
+    @LayoutlibDelegate
+    /*package*/ static MenuItemImpl createNewMenuItem(MenuBuilder thisMenu, int group, int id,
+            int categoryOrder, int ordering, CharSequence title, int defaultShowAsAction) {
+        return new BridgeMenuItemImpl(thisMenu, group, id, categoryOrder, ordering, title,
+                defaultShowAsAction);
+    }
+}
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 afcadef..9787432 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
@@ -28,7 +28,6 @@
 import com.android.ide.common.rendering.api.IAnimationListener;
 import com.android.ide.common.rendering.api.ILayoutPullParser;
 import com.android.ide.common.rendering.api.IProjectCallback;
-import com.android.ide.common.rendering.api.RenderParams;
 import com.android.ide.common.rendering.api.RenderResources;
 import com.android.ide.common.rendering.api.RenderSession;
 import com.android.ide.common.rendering.api.ResourceReference;
@@ -39,6 +38,12 @@
 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.internal.view.menu.ActionMenuItemView;
+import com.android.internal.view.menu.BridgeMenuItemImpl;
+import com.android.internal.view.menu.IconMenuItemView;
+import com.android.internal.view.menu.ListMenuItemView;
+import com.android.internal.view.menu.MenuItemImpl;
+import com.android.internal.view.menu.MenuView;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
@@ -101,11 +106,10 @@
 
 /**
  * Class implementing the render session.
- *
+ * <p/>
  * A session is a stateful representation of a layout file. It is initialized with data coming
  * through the {@link Bridge} API to inflate the layout. Further actions and rendering can then
  * be done on the layout.
- *
  */
 public class RenderSessionImpl extends RenderAction<SessionParams> {
 
@@ -172,7 +176,7 @@
     @Override
     public Result init(long timeout) {
         Result result = super.init(timeout);
-        if (result.isSuccess() == false) {
+        if (!result.isSuccess()) {
             return result;
         }
 
@@ -196,6 +200,7 @@
 
         // FIXME: find those out, and possibly add them to the render params
         boolean hasNavigationBar = true;
+        //noinspection ConstantConditions
         IWindowManager iwm = new IWindowManagerImpl(getContext().getConfiguration(),
                 metrics, Surface.ROTATION_0,
                 hasNavigationBar);
@@ -229,10 +234,9 @@
             BridgeContext context = getContext();
             boolean isRtl = Bridge.isLocaleRtl(params.getLocale());
             int layoutDirection = isRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR;
-            ActionBarLayout actionBar = null;
 
             // the view group that receives the window background.
-            ViewGroup backgroundView = null;
+            ViewGroup backgroundView;
 
             if (mWindowIsFloating || params.isForceNoDecor()) {
                 backgroundView = mViewRoot = mContentRoot = new FrameLayout(context);
@@ -266,7 +270,7 @@
                         NavigationBar navigationBar = createNavigationBar(context,
                                 hardwareConfig.getDensity(), isRtl, params.isRtlSupported());
                         topLayout.addView(navigationBar);
-                    } catch (XmlPullParserException e) {
+                    } catch (XmlPullParserException ignored) {
 
                     }
                 }
@@ -322,7 +326,7 @@
                         StatusBar statusBar = createStatusBar(context, hardwareConfig.getDensity(),
                                 layoutDirection, params.isRtlSupported());
                         topLayout.addView(statusBar);
-                    } catch (XmlPullParserException e) {
+                    } catch (XmlPullParserException ignored) {
 
                     }
                 }
@@ -339,20 +343,16 @@
 
                 // if the theme says no title/action bar, then the size will be 0
                 if (mActionBarSize > 0) {
-                    try {
-                        actionBar = createActionBar(context, params);
-                        backgroundLayout.addView(actionBar);
-                        actionBar.createMenuPopup();
-                        mContentRoot = actionBar.getContentRoot();
-                    } catch (XmlPullParserException e) {
-
-                    }
+                    ActionBarLayout actionBar = createActionBar(context, params);
+                    backgroundLayout.addView(actionBar);
+                    actionBar.createMenuPopup();
+                    mContentRoot = actionBar.getContentRoot();
                 } else if (mTitleBarSize > 0) {
                     try {
                         TitleBar titleBar = createTitleBar(context,
                                 hardwareConfig.getDensity(), params.getAppLabel());
                         backgroundLayout.addView(titleBar);
-                    } catch (XmlPullParserException e) {
+                    } catch (XmlPullParserException ignored) {
 
                     }
                 }
@@ -374,7 +374,7 @@
                         NavigationBar navigationBar = createNavigationBar(context,
                                 hardwareConfig.getDensity(), isRtl, params.isRtlSupported());
                         topLayout.addView(navigationBar);
-                    } catch (XmlPullParserException e) {
+                    } catch (XmlPullParserException ignored) {
 
                     }
                 }
@@ -399,7 +399,7 @@
             postInflateProcess(view, params.getProjectCallback());
 
             // get the background drawable
-            if (mWindowBackground != null && backgroundView != null) {
+            if (mWindowBackground != null) {
                 Drawable d = ResourceHelper.getDrawable(mWindowBackground, context);
                 backgroundView.setBackground(d);
             }
@@ -476,6 +476,7 @@
 
                     // first measure the full layout, with EXACTLY to get the size of the
                     // content as it is inside the decor/dialog
+                    @SuppressWarnings("deprecation")
                     Pair<Integer, Integer> exactMeasure = measureView(
                             mViewRoot, mContentRoot.getChildAt(0),
                             mMeasuredScreenWidth, MeasureSpec.EXACTLY,
@@ -483,6 +484,7 @@
 
                     // now measure the content only using UNSPECIFIED (where applicable, based on
                     // the rendering mode). This will give us the size the content needs.
+                    @SuppressWarnings("deprecation")
                     Pair<Integer, Integer> result = measureView(
                             mContentRoot, mContentRoot.getChildAt(0),
                             mMeasuredScreenWidth, widthMeasureSpecMode,
@@ -558,7 +560,7 @@
                     mCanvas.setDensity(hardwareConfig.getDensity().getDpiValue());
                 }
 
-                if (freshRender && newImage == false) {
+                if (freshRender && !newImage) {
                     Graphics2D gc = mImage.createGraphics();
                     gc.setComposite(AlphaComposite.Src);
 
@@ -573,7 +575,8 @@
                 mViewRoot.draw(mCanvas);
             }
 
-            mSystemViewInfoList = visitAllChildren(mViewRoot, 0, params.getExtendedViewInfoMode(), false);
+            mSystemViewInfoList = visitAllChildren(mViewRoot, 0, params.getExtendedViewInfoMode(),
+                    false);
 
             // success!
             return SUCCESS.createResult();
@@ -603,6 +606,7 @@
      * @param heightMode the MeasureSpec mode to use for the height.
      * @return the measured width/height if measuredView is non-null, null otherwise.
      */
+    @SuppressWarnings("deprecation")  // For the use of Pair
     private Pair<Integer, Integer> measureView(ViewGroup viewToMeasure, View measuredView,
             int width, int widthMode, int height, int heightMode) {
         int w_spec = MeasureSpec.makeMeasureSpec(width, widthMode);
@@ -633,7 +637,7 @@
         BridgeContext context = getContext();
 
         // find the animation file.
-        ResourceValue animationResource = null;
+        ResourceValue animationResource;
         int animationId = 0;
         if (isFrameworkAnimation) {
             animationResource = context.getRenderResources().getFrameworkResource(
@@ -723,7 +727,7 @@
 
         // add it to the parentView in the correct location
         Result result = addView(parentView, child, index);
-        if (result.isSuccess() == false) {
+        if (!result.isSuccess()) {
             return result;
         }
 
@@ -793,13 +797,13 @@
                     public void run() {
                         Result result = moveView(previousParent, newParentView, childView, index,
                                 params);
-                        if (result.isSuccess() == false) {
+                        if (!result.isSuccess()) {
                             listener.done(result);
                         }
 
                         // ready to do the work, acquire the scene.
                         result = acquire(250);
-                        if (result.isSuccess() == false) {
+                        if (!result.isSuccess()) {
                             listener.done(result);
                             return;
                         }
@@ -857,7 +861,7 @@
         }
 
         Result result = moveView(previousParent, newParentView, childView, index, layoutParams);
-        if (result.isSuccess() == false) {
+        if (!result.isSuccess()) {
             return result;
         }
 
@@ -991,7 +995,7 @@
         }
 
         Result result = removeView(parent, childView);
-        if (result.isSuccess() == false) {
+        if (!result.isSuccess()) {
             return result;
         }
 
@@ -1019,7 +1023,7 @@
 
 
     private void findBackground(RenderResources resources) {
-        if (getParams().isBgColorOverridden() == false) {
+        if (!getParams().isBgColorOverridden()) {
             mWindowBackground = resources.findItemInTheme("windowBackground",
                     true /*isFrameworkAttr*/);
             if (mWindowBackground != null) {
@@ -1036,7 +1040,7 @@
         boolean windowFullscreen = getBooleanThemeValue(resources,
                 "windowFullscreen", false /*defaultValue*/);
 
-        if (windowFullscreen == false && mWindowIsFloating == false) {
+        if (!windowFullscreen && !mWindowIsFloating) {
             // default value
             mStatusBarSize = DEFAULT_STATUS_BAR_HEIGHT;
 
@@ -1090,7 +1094,7 @@
             boolean windowNoTitle = getBooleanThemeValue(resources,
                     "windowNoTitle", false /*defaultValue*/);
 
-            if (windowNoTitle == false) {
+            if (!windowNoTitle) {
 
                 // default size of the window title bar
                 mTitleBarSize = DEFAULT_TITLE_BAR_HEIGHT;
@@ -1117,7 +1121,7 @@
     }
 
     private void findNavigationBar(RenderResources resources, DisplayMetrics metrics) {
-        if (hasSoftwareButtons() && mWindowIsFloating == false) {
+        if (hasSoftwareButtons() && !mWindowIsFloating) {
 
             // default value
             mNavigationBarSize = 48; // ??
@@ -1131,15 +1135,12 @@
                 int shortSize = hardwareConfig.getScreenHeight();
 
                 // compute in dp
-                int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / hardwareConfig.getDensity().getDpiValue();
+                int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT /
+                        hardwareConfig.getDensity().getDpiValue();
 
-                if (shortSizeDp < 600) {
-                    // 0-599dp: "phone" UI with bar on the side
-                    barOnBottom = false;
-                } else {
-                    // 600+dp: "tablet" UI with bar on the bottom
-                    barOnBottom = true;
-                }
+                // 0-599dp: "phone" UI with bar on the side
+                // 600+dp: "tablet" UI with bar on the bottom
+                barOnBottom = shortSizeDp >= 600;
             }
 
             if (barOnBottom) {
@@ -1190,13 +1191,15 @@
     }
 
     /**
-     * Post process on a view hierachy that was just inflated.
-     * <p/>At the moment this only support TabHost: If {@link TabHost} is detected, look for the
+     * Post process on a view hierarchy that was just inflated.
+     * <p/>
+     * At the moment this only supports TabHost: If {@link TabHost} is detected, look for the
      * {@link TabWidget}, and the corresponding {@link FrameLayout} and make new tabs automatically
      * based on the content of the {@link FrameLayout}.
      * @param view the root view to process.
      * @param projectCallback callback to the project.
      */
+    @SuppressWarnings("deprecation")  // For the use of Pair
     private void postInflateProcess(View view, IProjectCallback projectCallback)
             throws PostInflateException {
         if (view instanceof TabHost) {
@@ -1299,7 +1302,7 @@
                     "TabHost requires a TabWidget with id \"android:id/tabs\".\n");
         }
 
-        if ((v instanceof TabWidget) == false) {
+        if (!(v instanceof TabWidget)) {
             throw new PostInflateException(String.format(
                     "TabHost requires a TabWidget with id \"android:id/tabs\".\n" +
                     "View found with id 'tabs' is '%s'", v.getClass().getCanonicalName()));
@@ -1308,12 +1311,14 @@
         v = tabHost.findViewById(android.R.id.tabcontent);
 
         if (v == null) {
-            // TODO: see if we can fake tabs even without the FrameLayout (same below when the framelayout is empty)
+            // TODO: see if we can fake tabs even without the FrameLayout (same below when the frameLayout is empty)
+            //noinspection SpellCheckingInspection
             throw new PostInflateException(
                     "TabHost requires a FrameLayout with id \"android:id/tabcontent\".");
         }
 
-        if ((v instanceof FrameLayout) == false) {
+        if (!(v instanceof FrameLayout)) {
+            //noinspection SpellCheckingInspection
             throw new PostInflateException(String.format(
                     "TabHost requires a FrameLayout with id \"android:id/tabcontent\".\n" +
                     "View found with id 'tabcontent' is '%s'", v.getClass().getCanonicalName()));
@@ -1321,7 +1326,7 @@
 
         FrameLayout content = (FrameLayout)v;
 
-        // now process the content of the framelayout and dynamically create tabs for it.
+        // now process the content of the frameLayout and dynamically create tabs for it.
         final int count = content.getChildCount();
 
         // this must be called before addTab() so that the TabHost searches its TabWidget
@@ -1339,13 +1344,13 @@
                         }
                     });
             tabHost.addTab(spec);
-            return;
         } else {
-            // for each child of the framelayout, add a new TabSpec
+            // for each child of the frameLayout, add a new TabSpec
             for (int i = 0 ; i < count ; i++) {
                 View child = content.getChildAt(i);
                 String tabSpec = String.format("tab_spec%d", i+1);
                 int id = child.getId();
+                @SuppressWarnings("deprecation")
                 Pair<ResourceType, String> resource = projectCallback.resolveResourceId(id);
                 String name;
                 if (resource != null) {
@@ -1468,13 +1473,13 @@
         ViewInfo result;
         if (isContentFrame) {
             result = new ViewInfo(view.getClass().getName(),
-                    getContext().getViewKey(view),
+                    getViewKey(view),
                     view.getLeft(), view.getTop() + offset, view.getRight(),
                     view.getBottom() + offset, view, view.getLayoutParams());
 
         } else {
             result = new SystemViewInfo(view.getClass().getName(),
-                    getContext().getViewKey(view),
+                    getViewKey(view),
                     view.getLeft(), view.getTop(), view.getRight(),
                     view.getBottom(), view, view.getLayoutParams());
         }
@@ -1495,6 +1500,32 @@
         return result;
     }
 
+    /**
+     * The cookie for menu items are stored in menu item and not in the map from View stored in
+     * BridgeContext.
+     */
+    private Object getViewKey(View view) {
+        BridgeContext context = getContext();
+        if (!(view instanceof MenuView.ItemView)) {
+            return context.getViewKey(view);
+        }
+        MenuItemImpl menuItem;
+        if (view instanceof ActionMenuItemView) {
+            menuItem = ((ActionMenuItemView) view).getItemData();
+        } else if (view instanceof ListMenuItemView) {
+            menuItem = ((ListMenuItemView) view).getItemData();
+        } else if (view instanceof IconMenuItemView) {
+            menuItem = ((IconMenuItemView) view).getItemData();
+        } else {
+            menuItem = null;
+        }
+        if (menuItem instanceof BridgeMenuItemImpl) {
+            return ((BridgeMenuItemImpl) menuItem).getViewCookie();
+        }
+
+        return null;
+    }
+
     private void invalidateRenderingSize() {
         mMeasuredScreenWidth = mMeasuredScreenHeight = -1;
     }
@@ -1545,8 +1576,7 @@
     /**
      * Creates the action bar. Also queries the project callback for missing information.
      */
-    private ActionBarLayout createActionBar(BridgeContext context, SessionParams params)
-            throws XmlPullParserException {
+    private ActionBarLayout createActionBar(BridgeContext context, SessionParams params) {
         ActionBarLayout actionBar = new ActionBarLayout(context, params);
         actionBar.setLayoutParams(new LinearLayout.LayoutParams(
                 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
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 e0c05de..986b911 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
@@ -143,6 +143,8 @@
         "android.view.ViewRootImpl#isInTouchMode",
         "android.view.WindowManagerGlobal#getWindowManagerService",
         "android.view.inputmethod.InputMethodManager#getInstance",
+        "android.view.MenuInflater#emptyMethod",
+        "com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
         "com.android.internal.util.XmlUtils#convertValueToInt",
         "com.android.internal.textservice.ITextServicesManager$Stub#asInterface",
     };