Merge "Fix disappearing ripple background, treat active ripple separately" into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index 6fbd40b..20f58e6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -905,6 +905,8 @@
     field public static final int multiprocess = 16842771; // 0x1010013
     field public static final int name = 16842755; // 0x1010003
     field public static final int navigationBarColor = 16843858; // 0x1010452
+    field public static final int navigationContentDescription = 16843970; // 0x10104c2
+    field public static final int navigationIcon = 16843969; // 0x10104c1
     field public static final int navigationMode = 16843471; // 0x10102cf
     field public static final int negativeButtonText = 16843254; // 0x10101f6
     field public static final int nestedScrollingEnabled = 16843830; // 0x1010436
@@ -1757,6 +1759,7 @@
     field public static final int list = 16908298; // 0x102000a
     field public static final int mask = 16908353; // 0x1020041
     field public static final int message = 16908299; // 0x102000b
+    field public static final int navigationBarBackground = 16908355; // 0x1020043
     field public static final int paste = 16908322; // 0x1020022
     field public static final int primary = 16908300; // 0x102000c
     field public static final int progress = 16908301; // 0x102000d
@@ -1765,6 +1768,7 @@
     field public static final int selectTextMode = 16908333; // 0x102002d
     field public static final int selectedIcon = 16908302; // 0x102000e
     field public static final int startSelectingText = 16908328; // 0x1020028
+    field public static final int statusBarBackground = 16908354; // 0x1020042
     field public static final int stopSelectingText = 16908329; // 0x1020029
     field public static final int summary = 16908304; // 0x1020010
     field public static final int switchInputMethod = 16908324; // 0x1020024
@@ -5746,7 +5750,7 @@
   }
 
   public final class UsageStatsManager {
-    method public android.util.ArrayMap<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
+    method public java.util.Map<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
     method public android.app.usage.UsageEvents queryEvents(long, long);
     method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long);
     field public static final int INTERVAL_BEST = 4; // 0x4
@@ -7285,8 +7289,8 @@
     method public void registerComponentCallbacks(android.content.ComponentCallbacks);
     method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
     method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
-    method public abstract void removeStickyBroadcast(android.content.Intent);
-    method public abstract void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
+    method public abstract deprecated void removeStickyBroadcast(android.content.Intent);
+    method public abstract deprecated void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
     method public abstract void revokeUriPermission(android.net.Uri, int);
     method public abstract void sendBroadcast(android.content.Intent);
     method public abstract void sendBroadcast(android.content.Intent, java.lang.String);
@@ -7295,10 +7299,10 @@
     method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String);
     method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
     method public abstract void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
-    method public abstract void sendStickyBroadcast(android.content.Intent);
-    method public abstract void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
-    method public abstract void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
-    method public abstract void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+    method public abstract deprecated void sendStickyBroadcast(android.content.Intent);
+    method public abstract deprecated void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
+    method public abstract deprecated void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+    method public abstract deprecated void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
     method public abstract void setTheme(int);
     method public abstract deprecated void setWallpaper(android.graphics.Bitmap) throws java.io.IOException;
     method public abstract deprecated void setWallpaper(java.io.InputStream) throws java.io.IOException;
@@ -8578,9 +8582,6 @@
     field public java.lang.String targetPackage;
   }
 
-  public class KeySet {
-  }
-
   public class LabeledIntent extends android.content.Intent {
     ctor public LabeledIntent(android.content.Intent, java.lang.String, int, int);
     ctor public LabeledIntent(android.content.Intent, java.lang.String, java.lang.CharSequence, int);
@@ -8816,7 +8817,6 @@
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
     method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public abstract android.content.pm.KeySet getKeySetByAlias(java.lang.String, java.lang.String);
     method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public abstract java.lang.String getNameForUid(int);
@@ -8836,15 +8836,12 @@
     method public abstract android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public abstract android.content.pm.KeySet getSigningKeySet(java.lang.String);
     method public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
     method public abstract java.lang.String[] getSystemSharedLibraryNames();
     method public abstract java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract boolean hasSystemFeature(java.lang.String);
     method public abstract boolean isSafeMode();
-    method public abstract boolean isSignedBy(java.lang.String, android.content.pm.KeySet);
-    method public abstract boolean isSignedByExactly(java.lang.String, android.content.pm.KeySet);
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
     method public abstract java.util.List<android.content.pm.ProviderInfo> queryContentProviders(java.lang.String, int, int);
     method public abstract java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(java.lang.String, int);
@@ -17197,10 +17194,6 @@
     field public static final android.os.Parcelable.Creator CREATOR;
   }
 
-  public abstract interface NetworkBoundURLFactory {
-    method public abstract java.net.URL getBoundURL(android.net.Network, java.net.URL) throws java.net.MalformedURLException;
-  }
-
   public final class NetworkCapabilities implements android.os.Parcelable {
     ctor public NetworkCapabilities(android.net.NetworkCapabilities);
     method public int describeContents();
@@ -27196,16 +27189,14 @@
 package android.service.voice {
 
   public class AlwaysOnHotwordDetector {
-    method public android.content.Intent getManageIntent(int);
+    method public android.content.Intent createIntentToEnroll();
+    method public android.content.Intent createIntentToReEnroll();
+    method public android.content.Intent createIntentToUnEnroll();
     method public int getSupportedRecognitionModes();
     method public boolean startRecognition(int);
     method public boolean stopRecognition();
-    field public static final int MANAGE_ACTION_ENROLL = 0; // 0x0
-    field public static final int MANAGE_ACTION_RE_ENROLL = 1; // 0x1
-    field public static final int MANAGE_ACTION_UN_ENROLL = 2; // 0x2
     field public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 2; // 0x2
     field public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 1; // 0x1
-    field public static final int RECOGNITION_FLAG_NONE = 0; // 0x0
     field public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 2; // 0x2
     field public static final int RECOGNITION_MODE_VOICE_TRIGGER = 1; // 0x1
     field public static final int STATE_HARDWARE_UNAVAILABLE = -2; // 0xfffffffe
@@ -27214,7 +27205,8 @@
     field public static final int STATE_KEYPHRASE_UNSUPPORTED = -1; // 0xffffffff
   }
 
-  public static abstract interface AlwaysOnHotwordDetector.Callback {
+  public static abstract class AlwaysOnHotwordDetector.Callback {
+    ctor public AlwaysOnHotwordDetector.Callback();
     method public abstract void onAvailabilityChanged(int);
     method public abstract void onDetected(android.service.voice.AlwaysOnHotwordDetector.EventPayload);
     method public abstract void onError();
@@ -28421,6 +28413,7 @@
   }
 
   public static class PhoneAccount.Builder {
+    ctor public PhoneAccount.Builder();
     method public android.telecomm.PhoneAccount build();
     method public android.telecomm.PhoneAccount.Builder withAccountHandle(android.telecomm.PhoneAccountHandle);
     method public android.telecomm.PhoneAccount.Builder withCapabilities(int);
@@ -29805,7 +29798,6 @@
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
     method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public android.content.pm.KeySet getKeySetByAlias(java.lang.String, java.lang.String);
     method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public java.lang.String getNameForUid(int);
@@ -29824,15 +29816,12 @@
     method public android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo);
     method public android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public android.content.pm.KeySet getSigningKeySet(java.lang.String);
     method public android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
     method public java.lang.String[] getSystemSharedLibraryNames();
     method public java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public boolean hasSystemFeature(java.lang.String);
     method public boolean isSafeMode();
-    method public boolean isSignedBy(java.lang.String, android.content.pm.KeySet);
-    method public boolean isSignedByExactly(java.lang.String, android.content.pm.KeySet);
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
     method public java.util.List<android.content.pm.ProviderInfo> queryContentProviders(java.lang.String, int, int);
     method public java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(java.lang.String, int);
@@ -39590,22 +39579,6 @@
 
 }
 
-package com.android.internal.telecomm {
-
-  public abstract interface RemoteServiceCallback implements android.os.IInterface {
-    method public abstract void onError() throws android.os.RemoteException;
-    method public abstract void onResult(java.util.List<android.content.ComponentName>, java.util.List<android.os.IBinder>) throws android.os.RemoteException;
-  }
-
-  public static abstract class RemoteServiceCallback.Stub extends android.os.Binder implements com.android.internal.telecomm.RemoteServiceCallback {
-    ctor public RemoteServiceCallback.Stub();
-    method public android.os.IBinder asBinder();
-    method public static com.android.internal.telecomm.RemoteServiceCallback asInterface(android.os.IBinder);
-    method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
-  }
-
-}
-
 package com.android.internal.util {
 
   public abstract interface Predicate {
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 74ccbc2..6e77e132 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -161,17 +161,17 @@
     LOG_ALWAYS_FATAL_IF((numChars >= PATH_MAX || numChars < 0),
             "Error constructing dalvik cache : %s", strerror(errno));
 
-    int result = mkdir(dalvikCacheDir, 0771);
+    int result = mkdir(dalvikCacheDir, 0711);
     LOG_ALWAYS_FATAL_IF((result < 0 && errno != EEXIST),
             "Error creating cache dir %s : %s", dalvikCacheDir, strerror(errno));
 
     // We always perform these steps because the directory might
     // already exist, with wider permissions and a different owner
     // than we'd like.
-    result = chown(dalvikCacheDir, AID_SYSTEM, AID_SYSTEM);
+    result = chown(dalvikCacheDir, AID_ROOT, AID_ROOT);
     LOG_ALWAYS_FATAL_IF((result < 0), "Error changing dalvik-cache ownership : %s", strerror(errno));
 
-    result = chmod(dalvikCacheDir, 0771);
+    result = chmod(dalvikCacheDir, 0711);
     LOG_ALWAYS_FATAL_IF((result < 0),
             "Error changing dalvik-cache permissions : %s", strerror(errno));
 }
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index d9b40b1..da34094 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -866,6 +866,7 @@
 
     private void runInstall() {
         int installFlags = PackageManager.INSTALL_ALL_USERS;
+        int userId = UserHandle.USER_ALL;
         String installerPackageName = null;
 
         String opt;
@@ -909,6 +910,13 @@
                 }
             } else if (opt.equals("--abi")) {
                 abi = checkAbiArgument(nextOptionData());
+            } else if (opt.equals("--user")) {
+                userId = Integer.parseInt(nextOptionData());
+                if (userId == UserHandle.USER_ALL) {
+                    installFlags |= PackageManager.INSTALL_ALL_USERS;
+                } else {
+                    installFlags &= ~PackageManager.INSTALL_ALL_USERS;
+                }
             } else {
                 System.err.println("Error: Unknown option: " + opt);
                 return;
@@ -953,8 +961,8 @@
             VerificationParams verificationParams = new VerificationParams(verificationURI,
                     originatingURI, referrerURI, VerificationParams.NO_UID, null);
 
-            mPm.installPackage(apkFilePath, obs.getBinder(), installFlags, installerPackageName,
-                    verificationParams, abi);
+            mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags, installerPackageName,
+                    verificationParams, abi, userId);
 
             synchronized (obs) {
                 while (!obs.finished) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 9342ae5..6843827 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1494,57 +1494,52 @@
         return false;
     }
 
+    /** @hide */
     @Override
     public KeySet getKeySetByAlias(String packageName, String alias) {
         Preconditions.checkNotNull(packageName);
         Preconditions.checkNotNull(alias);
-        IBinder keySetToken;
+        KeySet ks;
         try {
-            keySetToken = mPM.getKeySetByAlias(packageName, alias);
+            ks = mPM.getKeySetByAlias(packageName, alias);
         } catch (RemoteException e) {
             return null;
         }
-        if (keySetToken == null) {
-            return null;
-        }
-        return new KeySet(keySetToken);
+        return ks;
     }
 
+    /** @hide */
     @Override
     public KeySet getSigningKeySet(String packageName) {
         Preconditions.checkNotNull(packageName);
-        IBinder keySetToken;
+        KeySet ks;
         try {
-            keySetToken = mPM.getSigningKeySet(packageName);
+            ks = mPM.getSigningKeySet(packageName);
         } catch (RemoteException e) {
             return null;
         }
-        if (keySetToken == null) {
-            return null;
-        }
-        return new KeySet(keySetToken);
+        return ks;
     }
 
-
+    /** @hide */
     @Override
     public boolean isSignedBy(String packageName, KeySet ks) {
         Preconditions.checkNotNull(packageName);
         Preconditions.checkNotNull(ks);
-        IBinder keySetToken = ks.getToken();
         try {
-            return mPM.isPackageSignedByKeySet(packageName, keySetToken);
+            return mPM.isPackageSignedByKeySet(packageName, ks);
         } catch (RemoteException e) {
             return false;
         }
     }
 
+    /** @hide */
     @Override
     public boolean isSignedByExactly(String packageName, KeySet ks) {
         Preconditions.checkNotNull(packageName);
         Preconditions.checkNotNull(ks);
-        IBinder keySetToken = ks.getToken();
         try {
-            return mPM.isPackageSignedByKeySetExactly(packageName, keySetToken);
+            return mPM.isPackageSignedByKeySetExactly(packageName, ks);
         } catch (RemoteException e) {
             return false;
         }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 214f50c..dbd180f 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -23,6 +23,7 @@
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
 import android.net.Uri;
+import android.os.Bundle;
 import android.service.notification.Condition;
 import android.service.notification.IConditionListener;
 import android.service.notification.IConditionProvider;
@@ -58,13 +59,15 @@
     void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id);
     void cancelNotificationsFromListener(in INotificationListener token, in String[] keys);
 
-    ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys);
+    ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys, int trim);
     void requestHintsFromListener(in INotificationListener token, int hints);
     int getHintsFromListener(in INotificationListener token);
     void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter);
     int getInterruptionFilterFromListener(in INotificationListener token);
+    void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
 
     ComponentName getEffectsSuppressor();
+    boolean matchesCallFilter(in Bundle extras);
 
     ZenModeConfig getZenModeConfig();
     boolean setZenModeConfig(in ZenModeConfig config);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e1dc8bf..966d2ce 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1424,6 +1424,8 @@
             extras.remove(Notification.EXTRA_LARGE_ICON);
             extras.remove(Notification.EXTRA_LARGE_ICON_BIG);
             extras.remove(Notification.EXTRA_PICTURE);
+            // Prevent light notifications from being rebuilt.
+            extras.remove(Builder.EXTRA_NEEDS_REBUILD);
         }
     }
 
@@ -1868,6 +1870,15 @@
         private Notification mRebuildNotification = null;
 
         /**
+         * Whether the build notification has three lines. This is used to make the top padding for
+         * both the contracted and expanded layout consistent.
+         *
+         * <p>
+         * This field is only valid during the build phase.
+         */
+        private boolean mHasThreeLines;
+
+        /**
          * Constructs a new Builder with the defaults:
          *
 
@@ -2564,19 +2575,23 @@
             return this;
         }
 
-        private Bitmap getProfileBadge() {
+        private Drawable getProfileBadgeDrawable() {
             // Note: This assumes that the current user can read the profile badge of the
             // originating user.
             UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-            Drawable badge = userManager.getBadgeForUser(new UserHandle(mOriginatingUserId), 0);
+            return userManager.getBadgeForUser(new UserHandle(mOriginatingUserId), 0);
+        }
+
+        private Bitmap getProfileBadge() {
+            Drawable badge = getProfileBadgeDrawable();
             if (badge == null) {
                 return null;
             }
-            final int width = badge.getIntrinsicWidth();
-            final int height = badge.getIntrinsicHeight();
-            Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+            final int size = mContext.getResources().getDimensionPixelSize(
+                    R.dimen.notification_badge_size);
+            Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
             Canvas canvas = new Canvas(bitmap);
-            badge.setBounds(0, 0, width, height);
+            badge.setBounds(0, 0, size, size);
             badge.draw(canvas);
             return bitmap;
         }
@@ -2602,6 +2617,12 @@
             return false;
         }
 
+        private void shrinkLine3Text(RemoteViews contentView) {
+            float subTextSize = mContext.getResources().getDimensionPixelSize(
+                    R.dimen.notification_subtext_size);
+            contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
+        }
+
         private RemoteViews applyStandardTemplate(int resId) {
             RemoteViews contentView = new BuilderRemoteViews(mContext.getPackageName(),
                     mOriginatingUserId, resId);
@@ -2674,10 +2695,7 @@
             if (showLine2) {
 
                 // need to shrink all the type to make sure everything fits
-                final Resources res = mContext.getResources();
-                final float subTextSize = res.getDimensionPixelSize(
-                        R.dimen.notification_subtext_size);
-                contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
+                shrinkLine3Text(contentView);
             }
 
             if (mWhen != 0 && mShowWhen) {
@@ -2696,7 +2714,7 @@
 
             // Adjust padding depending on line count and font size.
             contentView.setViewPadding(R.id.line1, 0, calculateTopPadding(mContext,
-                    hasThreeLines(), mContext.getResources().getConfiguration().fontScale),
+                    mHasThreeLines, mContext.getResources().getConfiguration().fontScale),
                     0, 0);
 
             // We want to add badge to first line of text.
@@ -2721,7 +2739,12 @@
          *         is going to have one or two lines
          */
         private boolean hasThreeLines() {
-            boolean hasLine3 = mContentText != null || mContentInfo != null || mNumber > 0;
+            boolean contentTextInLine2 = mSubText != null && mContentText != null;
+
+            // If we have content text in line 2, badge goes into line 2, or line 3 otherwise
+            boolean badgeInLine3 = getProfileBadgeDrawable() != null && !contentTextInLine2;
+            boolean hasLine3 = mContentText != null || mContentInfo != null || mNumber > 0
+                    || badgeInLine3;
             boolean hasLine2 = (mSubText != null && mContentText != null) ||
                     (mSubText == null && (mProgressMax != 0 || mProgressIndeterminate));
             return hasLine2 && hasLine3;
@@ -3092,6 +3115,7 @@
             if (mRebuildNotification == null) {
                 throw new IllegalStateException("rebuild() only valid when in 'rebuild' mode.");
             }
+            mHasThreeLines = hasThreeLines();
 
             Bundle extras = mRebuildNotification.extras;
 
@@ -3124,6 +3148,7 @@
             }
             extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW);
 
+            mHasThreeLines = false;
             return mRebuildNotification;
         }
 
@@ -3238,6 +3263,7 @@
          */
         public Notification build() {
             mOriginatingUserId = mContext.getUserId();
+            mHasThreeLines = hasThreeLines();
 
             Notification n = buildUnstyled();
 
@@ -3259,6 +3285,7 @@
                 mStyle.addExtras(n.extras);
             }
 
+            mHasThreeLines = false;
             return n;
         }
 
@@ -3388,7 +3415,7 @@
          */
         protected void applyTopPadding(RemoteViews contentView) {
             int topPadding = Builder.calculateTopPadding(mBuilder.mContext,
-                    mBuilder.hasThreeLines(),
+                    mBuilder.mHasThreeLines,
                     mBuilder.mContext.getResources().getConfiguration().fontScale);
             contentView.setViewPadding(R.id.line1, 0, topPadding, 0, 0);
         }
@@ -3661,6 +3688,8 @@
 
             applyTopPadding(contentView);
 
+            mBuilder.shrinkLine3Text(contentView);
+
             mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
 
             return contentView;
@@ -3800,6 +3829,8 @@
 
             applyTopPadding(contentView);
 
+            mBuilder.shrinkLine3Text(contentView);
+
             mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
 
             return contentView;
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index fc047de..7dc1ad64 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -20,6 +20,7 @@
 import android.app.Notification.Builder;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -251,5 +252,17 @@
         }
     }
 
+    /**
+     * @hide
+     */
+    public boolean matchesCallFilter(Bundle extras) {
+        INotificationManager service = getService();
+        try {
+            return service.matchesCallFilter(extras);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
     private Context mContext;
 }
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 2431ad0..fb80de2 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -106,7 +106,9 @@
         }
 
         /**
-         * The time at which this event occurred.
+         * The time at which this event occurred, measured in milliseconds since the epoch.
+         * <p/>
+         * See {@link System#currentTimeMillis()}.
          */
         public long getTimeStamp() {
             return mTimeStamp;
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index e47a802..abfc435 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -81,28 +81,36 @@
     }
 
     /**
-     * Get the beginning of the time range this {@link android.app.usage.UsageStats} represents.
+     * Get the beginning of the time range this {@link android.app.usage.UsageStats} represents,
+     * measured in milliseconds since the epoch.
+     * <p/>
+     * See {@link System#currentTimeMillis()}.
      */
     public long getFirstTimeStamp() {
         return mBeginTimeStamp;
     }
 
     /**
-     * Get the end of the time range this {@link android.app.usage.UsageStats} represents.
+     * Get the end of the time range this {@link android.app.usage.UsageStats} represents,
+     * measured in milliseconds since the epoch.
+     * <p/>
+     * See {@link System#currentTimeMillis()}.
      */
     public long getLastTimeStamp() {
         return mEndTimeStamp;
     }
 
     /**
-     * Get the last time this package was used.
+     * Get the last time this package was used, measured in milliseconds since the epoch.
+     * <p/>
+     * See {@link System#currentTimeMillis()}.
      */
     public long getLastTimeUsed() {
         return mLastTimeUsed;
     }
 
     /**
-     * Get the total time this package spent in the foreground.
+     * Get the total time this package spent in the foreground, measured in milliseconds.
      */
     public long getTotalTimeInForeground() {
         return mTotalTimeInForeground;
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index f9b8928..5830fcf 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -23,6 +23,7 @@
 
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Provides access to device usage history and statistics. Usage data is aggregated into
@@ -149,7 +150,6 @@
      * @param endTime The exclusive end of the range of events to include in the results.
      * @return A {@link UsageEvents}.
      */
-    @SuppressWarnings("unchecked")
     public UsageEvents queryEvents(long beginTime, long endTime) {
         try {
             UsageEvents iter = mService.queryEvents(beginTime, endTime,
@@ -170,15 +170,13 @@
      *
      * @param beginTime The inclusive beginning of the range of stats to include in the results.
      * @param endTime The exclusive end of the range of stats to include in the results.
-     * @return An {@link android.util.ArrayMap} keyed by package name or null if no stats are
+     * @return A {@link java.util.Map} keyed by package name, or null if no stats are
      *         available.
      */
-    public ArrayMap<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime) {
+    public Map<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime) {
         List<UsageStats> stats = queryUsageStats(INTERVAL_BEST, beginTime, endTime);
         if (stats.isEmpty()) {
-            @SuppressWarnings("unchecked")
-            ArrayMap<String, UsageStats> emptyStats = ArrayMap.EMPTY;
-            return emptyStats;
+            return Collections.emptyMap();
         }
 
         ArrayMap<String, UsageStats> aggregatedStats = new ArrayMap<>();
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b7d7c25..f979a0c 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1486,8 +1486,6 @@
      * @see #sendBroadcast(Intent)
      * @see #sendBroadcast(Intent, String)
      * @see #sendOrderedBroadcast(Intent, String)
-     * @see #sendStickyBroadcast(Intent)
-     * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
      * @see android.content.BroadcastReceiver
      * @see #registerReceiver
      * @see android.app.Activity#RESULT_OK
@@ -1584,7 +1582,7 @@
             @Nullable  Bundle initialExtras);
 
     /**
-     * Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the
+     * <p>Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the
      * Intent you are sending stays around after the broadcast is complete,
      * so that others can quickly retrieve that data through the return
      * value of {@link #registerReceiver(BroadcastReceiver, IntentFilter)}.  In
@@ -1595,6 +1593,12 @@
      * permission in order to use this API.  If you do not hold that
      * permission, {@link SecurityException} will be thrown.
      *
+     * @deprecated Sticky broadcasts should not be used.  They provide no security (anyone
+     * can access them), no protection (anyone can modify them), and many other problems.
+     * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+     * has changed, with another mechanism for apps to retrieve the current value whenever
+     * desired.
+     *
      * @param intent The Intent to broadcast; all receivers matching this
      * Intent will receive the broadcast, and the Intent will be held to
      * be re-broadcast to future receivers.
@@ -1602,10 +1606,11 @@
      * @see #sendBroadcast(Intent)
      * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
      */
+    @Deprecated
     public abstract void sendStickyBroadcast(Intent intent);
 
     /**
-     * Version of {@link #sendStickyBroadcast} that allows you to
+     * <p>Version of {@link #sendStickyBroadcast} that allows you to
      * receive data back from the broadcast.  This is accomplished by
      * supplying your own BroadcastReceiver when calling, which will be
      * treated as a final receiver at the end of the broadcast -- its
@@ -1622,6 +1627,12 @@
      *
      * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
      *
+     * @deprecated Sticky broadcasts should not be used.  They provide no security (anyone
+     * can access them), no protection (anyone can modify them), and many other problems.
+     * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+     * has changed, with another mechanism for apps to retrieve the current value whenever
+     * desired.
+     *
      * @param intent The Intent to broadcast; all receivers matching this
      *               Intent will receive the broadcast.
      * @param resultReceiver Your own BroadcastReceiver to treat as the final
@@ -1644,31 +1655,45 @@
      * @see #registerReceiver
      * @see android.app.Activity#RESULT_OK
      */
+    @Deprecated
     public abstract void sendStickyOrderedBroadcast(Intent intent,
             BroadcastReceiver resultReceiver,
             @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
             @Nullable Bundle initialExtras);
 
     /**
-     * Remove the data previously sent with {@link #sendStickyBroadcast},
+     * <p>Remove the data previously sent with {@link #sendStickyBroadcast},
      * so that it is as if the sticky broadcast had never happened.
      *
      * <p>You must hold the {@link android.Manifest.permission#BROADCAST_STICKY}
      * permission in order to use this API.  If you do not hold that
      * permission, {@link SecurityException} will be thrown.
      *
+     * @deprecated Sticky broadcasts should not be used.  They provide no security (anyone
+     * can access them), no protection (anyone can modify them), and many other problems.
+     * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+     * has changed, with another mechanism for apps to retrieve the current value whenever
+     * desired.
+     *
      * @param intent The Intent that was previously broadcast.
      *
      * @see #sendStickyBroadcast
      */
+    @Deprecated
     public abstract void removeStickyBroadcast(Intent intent);
 
     /**
-     * Version of {@link #sendStickyBroadcast(Intent)} that allows you to specify the
+     * <p>Version of {@link #sendStickyBroadcast(Intent)} that allows you to specify the
      * user the broadcast will be sent to.  This is not available to applications
      * that are not pre-installed on the system image.  Using it requires holding
      * the INTERACT_ACROSS_USERS permission.
      *
+     * @deprecated Sticky broadcasts should not be used.  They provide no security (anyone
+     * can access them), no protection (anyone can modify them), and many other problems.
+     * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+     * has changed, with another mechanism for apps to retrieve the current value whenever
+     * desired.
+     *
      * @param intent The Intent to broadcast; all receivers matching this
      * Intent will receive the broadcast, and the Intent will be held to
      * be re-broadcast to future receivers.
@@ -1676,10 +1701,11 @@
      *
      * @see #sendBroadcast(Intent)
      */
+    @Deprecated
     public abstract void sendStickyBroadcastAsUser(Intent intent, UserHandle user);
 
     /**
-     * Version of
+     * <p>Version of
      * {@link #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)}
      * that allows you to specify the
      * user the broadcast will be sent to.  This is not available to applications
@@ -1688,6 +1714,12 @@
      *
      * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
      *
+     * @deprecated Sticky broadcasts should not be used.  They provide no security (anyone
+     * can access them), no protection (anyone can modify them), and many other problems.
+     * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+     * has changed, with another mechanism for apps to retrieve the current value whenever
+     * desired.
+     *
      * @param intent The Intent to broadcast; all receivers matching this
      *               Intent will receive the broadcast.
      * @param user UserHandle to send the intent to.
@@ -1705,13 +1737,14 @@
      *
      * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
      */
+    @Deprecated
     public abstract void sendStickyOrderedBroadcastAsUser(Intent intent,
             UserHandle user, BroadcastReceiver resultReceiver,
             @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
             @Nullable Bundle initialExtras);
 
     /**
-     * Version of {@link #removeStickyBroadcast(Intent)} that allows you to specify the
+     * <p>Version of {@link #removeStickyBroadcast(Intent)} that allows you to specify the
      * user the broadcast will be sent to.  This is not available to applications
      * that are not pre-installed on the system image.  Using it requires holding
      * the INTERACT_ACROSS_USERS permission.
@@ -1720,11 +1753,18 @@
      * permission in order to use this API.  If you do not hold that
      * permission, {@link SecurityException} will be thrown.
      *
+     * @deprecated Sticky broadcasts should not be used.  They provide no security (anyone
+     * can access them), no protection (anyone can modify them), and many other problems.
+     * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+     * has changed, with another mechanism for apps to retrieve the current value whenever
+     * desired.
+     *
      * @param intent The Intent that was previously broadcast.
      * @param user UserHandle to remove the sticky broadcast from.
      *
      * @see #sendStickyBroadcastAsUser
      */
+    @Deprecated
     public abstract void removeStickyBroadcastAsUser(Intent intent, UserHandle user);
 
     /**
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 44478d4..3e1f60a 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -32,6 +32,7 @@
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
+import android.content.pm.KeySet;
 import android.content.pm.PackageInfo;
 import android.content.pm.ManifestDigest;
 import android.content.pm.PackageCleanItem;
@@ -198,6 +199,14 @@
             in VerificationParams verificationParams,
             in String packageAbiOverride);
 
+    void installPackageAsUser(in String originPath,
+            in IPackageInstallObserver2 observer,
+            int flags,
+            in String installerPackageName,
+            in VerificationParams verificationParams,
+            in String packageAbiOverride,
+            int userId);
+
     void finishPackageInstall(int token);
 
     void setInstallerPackageName(in String targetPackage, in String installerPackageName);
@@ -446,8 +455,8 @@
     boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId);
     boolean getBlockUninstallForUser(String packageName, int userId);
 
-    IBinder getKeySetByAlias(String packageName, String alias);
-    IBinder getSigningKeySet(String packageName);
-    boolean isPackageSignedByKeySet(String packageName, IBinder ks);
-    boolean isPackageSignedByKeySetExactly(String packageName, IBinder ks);
+    KeySet getKeySetByAlias(String packageName, String alias);
+    KeySet getSigningKeySet(String packageName);
+    boolean isPackageSignedByKeySet(String packageName, in KeySet ks);
+    boolean isPackageSignedByKeySetExactly(String packageName, in KeySet ks);
 }
diff --git a/core/java/android/content/pm/KeySet.aidl b/core/java/android/content/pm/KeySet.aidl
new file mode 100644
index 0000000..493d288
--- /dev/null
+++ b/core/java/android/content/pm/KeySet.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.content.pm;
+
+parcelable KeySet;
\ No newline at end of file
diff --git a/core/java/android/content/pm/KeySet.java b/core/java/android/content/pm/KeySet.java
index fcdaa18..643db7e 100644
--- a/core/java/android/content/pm/KeySet.java
+++ b/core/java/android/content/pm/KeySet.java
@@ -17,13 +17,16 @@
 package android.content.pm;
 
 import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 /**
  * Represents a {@code KeySet} that has been declared in the AndroidManifest.xml
  * file for the application.  A {@code KeySet} can be used explicitly to
  * represent a trust relationship with other applications on the device.
+ * @hide
  */
-public class KeySet {
+public class KeySet implements Parcelable {
 
     private IBinder token;
 
@@ -40,6 +43,7 @@
         return token;
     }
 
+    /** @hide */
     @Override
     public boolean equals(Object o) {
         if (o instanceof KeySet) {
@@ -48,4 +52,58 @@
         }
         return false;
     }
+
+    /** @hide */
+    @Override
+    public int hashCode() {
+        return token.hashCode();
+    }
+
+    /**
+     * Implement Parcelable
+     * @hide
+     */
+    public static final Parcelable.Creator<KeySet> CREATOR
+            = new Parcelable.Creator<KeySet>() {
+
+        /**
+         * Create a KeySet from a Parcel
+         *
+         * @param in The parcel containing the KeySet
+         */
+        public KeySet createFromParcel(Parcel source) {
+            return readFromParcel(source);
+        }
+
+        /**
+         * Create an array of null KeySets
+         */
+        public KeySet[] newArray(int size) {
+            return new KeySet[size];
+        }
+    };
+
+    /**
+     * @hide
+     */
+    private static KeySet readFromParcel(Parcel in) {
+        IBinder token = in.readStrongBinder();
+        return new KeySet(token);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeStrongBinder(token);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index c928a18..44e24b1 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -623,7 +623,7 @@
             try {
                 final ParcelFileDescriptor clientSocket = mSession.openWrite(name,
                         offsetBytes, lengthBytes);
-                return new FileBridge.FileBridgeOutputStream(clientSocket.getFileDescriptor());
+                return new FileBridge.FileBridgeOutputStream(clientSocket);
             } catch (RuntimeException e) {
                 ExceptionUtils.maybeUnwrapIOException(e);
                 throw e;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index fa2bb4d..1b15ff5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3698,10 +3698,13 @@
      *
      * @param alias The alias for a given {@link KeySet} as defined in the
      *        application's AndroidManifest.xml.
+     * @hide
      */
     public abstract KeySet getKeySetByAlias(String packageName, String alias);
 
-    /** Return the signing {@link KeySet} for this application. */
+    /** Return the signing {@link KeySet} for this application. 
+     * @hide
+     */
     public abstract KeySet getSigningKeySet(String packageName);
 
     /**
@@ -3709,6 +3712,7 @@
      * of the keys specified by the {@link KeySet} ks.  This will return true if
      * the package has been signed by additional keys (a superset) as well.
      * Compare to {@link #isSignedByExactly(String packageName, KeySet ks)}.
+     * @hide
      */
     public abstract boolean isSignedBy(String packageName, KeySet ks);
 
@@ -3716,6 +3720,7 @@
      * Return whether the package denoted by packageName has been signed by all
      * of, and only, the keys specified by the {@link KeySet} ks. Compare to
      * {@link #isSignedBy(String packageName, KeySet ks)}.
+     * @hide
      */
     public abstract boolean isSignedByExactly(String packageName, KeySet ks);
 
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index ec4bc7d..0895fe3 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -19,6 +19,7 @@
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.impl.CaptureResultExtras;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -48,13 +49,23 @@
  */
 public final class TotalCaptureResult extends CaptureResult {
 
+    private final List<CaptureResult> mPartialResults;
+
     /**
-     * Takes ownership of the passed-in properties object
+     * Takes ownership of the passed-in camera metadata and the partial results
+     *
+     * @param partials a list of partial results; {@code null} will be substituted for an empty list
      * @hide
      */
     public TotalCaptureResult(CameraMetadataNative results, CaptureRequest parent,
-            CaptureResultExtras extras) {
+            CaptureResultExtras extras, List<CaptureResult> partials) {
         super(results, parent, extras);
+
+        if (partials == null) {
+            mPartialResults = new ArrayList<>();
+        } else {
+            mPartialResults = partials;
+        }
     }
 
     /**
@@ -65,6 +76,8 @@
      */
     public TotalCaptureResult(CameraMetadataNative results, int sequenceId) {
         super(results, sequenceId);
+
+        mPartialResults = new ArrayList<>();
     }
 
     /**
@@ -73,14 +86,13 @@
      * <p>The list is returned is unmodifiable; attempting to modify it will result in a
      * {@code UnsupportedOperationException} being thrown.</p>
      *
-     * <p>The list size will be inclusive between {@code 1} and
-     * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT}, in ascending order
+     * <p>The list size will be inclusive between {@code 0} and
+     * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT}, with elements in ascending order
      * of when {@link CameraCaptureSession.CaptureListener#onCaptureProgressed} was invoked.</p>
      *
      * @return unmodifiable list of partial results
      */
     public List<CaptureResult> getPartialResults() {
-        // TODO
-        return Collections.unmodifiableList(null);
+        return Collections.unmodifiableList(mPartialResults);
     }
 }
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index d75dfe6..f5666bf 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -41,6 +41,7 @@
 
 import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -967,6 +968,8 @@
 
         private long mCompletedFrameNumber = -1;
         private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
+        /** Map frame numbers to list of partial results */
+        private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>();
 
         private void update() {
             Iterator<Long> iter = mFutureErrorSet.iterator();
@@ -983,8 +986,8 @@
 
         /**
          * This function is called every time when a result or an error is received.
-         * @param frameNumber: the frame number corresponding to the result or error
-         * @param isError: true if it is an error, false if it is not an error
+         * @param frameNumber the frame number corresponding to the result or error
+         * @param isError true if it is an error, false if it is not an error
          */
         public void updateTracker(long frameNumber, boolean isError) {
             if (isError) {
@@ -1006,6 +1009,55 @@
             update();
         }
 
+        /**
+         * This function is called every time a result has been completed.
+         *
+         * <p>It keeps a track of all the partial results already created for a particular
+         * frame number.</p>
+         *
+         * @param frameNumber the frame number corresponding to the result
+         * @param result the total or partial result
+         * @param partial {@true} if the result is partial, {@code false} if total
+         */
+        public void updateTracker(long frameNumber, CaptureResult result, boolean partial) {
+
+            if (!partial) {
+                // Update the total result's frame status as being successful
+                updateTracker(frameNumber, /*isError*/false);
+                // Don't keep a list of total results, we don't need to track them
+                return;
+            }
+
+            if (result == null) {
+                // Do not record blank results; this also means there will be no total result
+                // so it doesn't matter that the partials were not recorded
+                return;
+            }
+
+            // Partial results must be aggregated in-order for that frame number
+            List<CaptureResult> partials = mPartialResults.get(frameNumber);
+            if (partials == null) {
+                partials = new ArrayList<>();
+                mPartialResults.put(frameNumber, partials);
+            }
+
+            partials.add(result);
+        }
+
+        /**
+         * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}.
+         *
+         * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker}
+         * is called again with new partials for that frame number).</p>
+         *
+         * @param frameNumber the frame number corresponding to the result
+         * @return a list of partial results for that frame with at least 1 element,
+         *         or {@code null} if there were no partials recorded for that frame
+         */
+        public List<CaptureResult> popPartialResults(long frameNumber) {
+            return mPartialResults.remove(frameNumber);
+        }
+
         public long getCompletedFrameNumber() {
             return mCompletedFrameNumber;
         }
@@ -1244,12 +1296,6 @@
                 boolean isPartialResult =
                         (resultExtras.getPartialResultCount() < mTotalPartialCount);
 
-                // Update tracker (increment counter) when it's not a partial result.
-                if (!isPartialResult) {
-                    mFrameNumberTracker.updateTracker(frameNumber,
-                            /*error*/false);
-                }
-
                 // Check if we have a listener for this
                 if (holder == null) {
                     if (DEBUG) {
@@ -1257,6 +1303,9 @@
                                 "holder is null, early return at frame "
                                         + frameNumber);
                     }
+
+                    mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
+
                     return;
                 }
 
@@ -1266,6 +1315,8 @@
                                 "camera is closed, early return at frame "
                                         + frameNumber);
                     }
+
+                    mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
                     return;
                 }
 
@@ -1273,6 +1324,8 @@
 
                 Runnable resultDispatch = null;
 
+                CaptureResult finalResult;
+
                 // Either send a partial result or the final capture completed result
                 if (isPartialResult) {
                     final CaptureResult resultAsCapture =
@@ -1290,9 +1343,14 @@
                             }
                         }
                     };
+
+                    finalResult = resultAsCapture;
                 } else {
+                    List<CaptureResult> partialResults =
+                            mFrameNumberTracker.popPartialResults(frameNumber);
+
                     final TotalCaptureResult resultAsCapture =
-                            new TotalCaptureResult(result, request, resultExtras);
+                            new TotalCaptureResult(result, request, resultExtras, partialResults);
 
                     // Final capture result
                     resultDispatch = new Runnable() {
@@ -1306,10 +1364,15 @@
                             }
                         }
                     };
+
+                    finalResult = resultAsCapture;
                 }
 
                 holder.getHandler().post(resultDispatch);
 
+                // Collect the partials for a total result; or mark the frame as totally completed
+                mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult);
+
                 // Fire onCaptureSequenceCompleted
                 if (!isPartialResult) {
                     checkAndFireSequenceComplete();
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index febb015..f47ce79 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -67,7 +67,6 @@
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
 
 /**
  * Implementation of camera metadata marshal/unmarshal across Binder to
@@ -655,6 +654,15 @@
 
     private Face[] getFaces() {
         Integer faceDetectMode = get(CaptureResult.STATISTICS_FACE_DETECT_MODE);
+        byte[] faceScores = get(CaptureResult.STATISTICS_FACE_SCORES);
+        Rect[] faceRectangles = get(CaptureResult.STATISTICS_FACE_RECTANGLES);
+        int[] faceIds = get(CaptureResult.STATISTICS_FACE_IDS);
+        int[] faceLandmarks = get(CaptureResult.STATISTICS_FACE_LANDMARKS);
+
+        if (areValuesAllNull(faceDetectMode, faceScores, faceRectangles, faceIds, faceLandmarks)) {
+            return null;
+        }
+
         if (faceDetectMode == null) {
             Log.w(TAG, "Face detect mode metadata is null, assuming the mode is SIMPLE");
             faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE;
@@ -670,8 +678,6 @@
         }
 
         // Face scores and rectangles are required by SIMPLE and FULL mode.
-        byte[] faceScores = get(CaptureResult.STATISTICS_FACE_SCORES);
-        Rect[] faceRectangles = get(CaptureResult.STATISTICS_FACE_RECTANGLES);
         if (faceScores == null || faceRectangles == null) {
             Log.w(TAG, "Expect face scores and rectangles to be non-null");
             return new Face[0];
@@ -683,8 +689,6 @@
         // To be safe, make number of faces is the minimal of all face info metadata length.
         int numFaces = Math.min(faceScores.length, faceRectangles.length);
         // Face id and landmarks are only required by FULL mode.
-        int[] faceIds = get(CaptureResult.STATISTICS_FACE_IDS);
-        int[] faceLandmarks = get(CaptureResult.STATISTICS_FACE_LANDMARKS);
         if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
             if (faceIds == null || faceLandmarks == null) {
                 Log.w(TAG, "Expect face ids and landmarks to be non-null for FULL mode," +
@@ -755,22 +759,32 @@
 
     private LensShadingMap getLensShadingMap() {
         float[] lsmArray = getBase(CaptureResult.STATISTICS_LENS_SHADING_MAP);
+        Size s = get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE);
+
+        // Do not warn if lsmArray is null while s is not. This is valid.
         if (lsmArray == null) {
-            Log.w(TAG, "getLensShadingMap - Lens shading map was null.");
             return null;
         }
-        Size s = get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE);
+
+        if (s == null) {
+            Log.w(TAG, "getLensShadingMap - Lens shading map size was null.");
+            return null;
+        }
+
         LensShadingMap map = new LensShadingMap(lsmArray, s.getHeight(), s.getWidth());
         return map;
     }
 
     private Location getGpsLocation() {
         String processingMethod = get(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
-        Location l = new Location(translateProcessToLocationProvider(processingMethod));
-
         double[] coords = get(CaptureResult.JPEG_GPS_COORDINATES);
         Long timeStamp = get(CaptureResult.JPEG_GPS_TIMESTAMP);
 
+        if (areValuesAllNull(processingMethod, coords, timeStamp)) {
+            return null;
+        }
+
+        Location l = new Location(translateProcessToLocationProvider(processingMethod));
         if (timeStamp != null) {
             l.setTime(timeStamp);
         } else {
@@ -873,7 +887,13 @@
         float[] red = getBase(CaptureRequest.TONEMAP_CURVE_RED);
         float[] green = getBase(CaptureRequest.TONEMAP_CURVE_GREEN);
         float[] blue = getBase(CaptureRequest.TONEMAP_CURVE_BLUE);
+
+        if (areValuesAllNull(red, green, blue)) {
+            return null;
+        }
+
         if (red == null || green == null || blue == null) {
+            Log.w(TAG, "getTonemapCurve - missing tone curve components");
             return null;
         }
         TonemapCurve tc = new TonemapCurve(red, green, blue);
@@ -1208,6 +1228,18 @@
         }
     }
 
+    /** Check if input arguments are all {@code null}.
+     *
+     * @param objs Input arguments for null check
+     * @return {@code true} if input arguments are all {@code null}, otherwise {@code false}
+     */
+    private static boolean areValuesAllNull(Object... objs) {
+        for (Object o : objs) {
+            if (o != null) return false;
+        }
+        return true;
+    }
+
     static {
         /*
          * We use a class initializer to allow the native code to cache some field offsets
diff --git a/core/java/android/hardware/hdmi/HdmiPortInfo.java b/core/java/android/hardware/hdmi/HdmiPortInfo.java
index 85e7531b..2ec6126 100644
--- a/core/java/android/hardware/hdmi/HdmiPortInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiPortInfo.java
@@ -166,7 +166,7 @@
     public String toString() {
         StringBuffer s = new StringBuffer();
         s.append("port_id: ").append(mId).append(", ");
-        s.append("address: ").append(mAddress).append(", ");
+        s.append("address: ").append(String.format("0x%04x", mAddress)).append(", ");
         s.append("cec: ").append(mCecSupported).append(", ");
         s.append("arc: ").append(mArcSupported).append(", ");
         s.append("mhl: ").append(mMhlSupported);
diff --git a/core/java/android/net/NetworkBoundURLFactory.java b/core/java/android/net/NetworkBoundURLFactory.java
deleted file mode 100644
index 356100e..0000000
--- a/core/java/android/net/NetworkBoundURLFactory.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.net;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-
-/**
- * An interface that describes a factory for network-specific {@link URL} objects.
- */
-public interface NetworkBoundURLFactory {
-    /**
-     * Returns a {@link URL} based on the given URL but bound to the specified {@code Network},
-     * such that opening the URL will send all network traffic on the specified Network.
-     *
-     * @return a {@link URL} bound to this {@code Network}.
-     * @throws MalformedURLException if the URL was not valid, or this factory cannot handle the
-     *         specified URL (e.g., if it does not support the protocol of the URL).
-     */
-    public URL getBoundURL(Network network, URL url) throws MalformedURLException;
-}
diff --git a/core/java/android/os/FileBridge.java b/core/java/android/os/FileBridge.java
index bf8d15c..022a106 100644
--- a/core/java/android/os/FileBridge.java
+++ b/core/java/android/os/FileBridge.java
@@ -131,10 +131,17 @@
     }
 
     public static class FileBridgeOutputStream extends OutputStream {
+        private final ParcelFileDescriptor mClientPfd;
         private final FileDescriptor mClient;
         private final byte[] mTemp = new byte[MSG_LENGTH];
 
+        public FileBridgeOutputStream(ParcelFileDescriptor clientPfd) {
+            mClientPfd = clientPfd;
+            mClient = clientPfd.getFileDescriptor();
+        }
+
         public FileBridgeOutputStream(FileDescriptor client) {
+            mClientPfd = null;
             mClient = client;
         }
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ae11f47..33e0468 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -904,6 +904,15 @@
     public static final String ACTION_APP_NOTIFICATION_SETTINGS
             = "android.settings.APP_NOTIFICATION_SETTINGS";
 
+    /**
+     * Activity Action: Show notification redaction settings.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_APP_NOTIFICATION_REDACTION
+            = "android.settings.ACTION_APP_NOTIFICATION_REDACTION";
+
     /** @hide */ public static final String EXTRA_APP_UID = "app_uid";
     /** @hide */ public static final String EXTRA_APP_PACKAGE = "app_package";
 
@@ -3714,6 +3723,14 @@
                 "lock_screen_allow_private_notifications";
 
         /**
+         * Set by the system to track if the user needs to see the call to action for
+         * the lockscreen notification policy.
+         * @hide
+         */
+        public static final String SHOW_NOTE_ABOUT_NOTIFICATION_HIDING =
+                "show_note_about_notification_hiding";
+
+        /**
          * The Logging ID (a unique 64-bit value) as a hex string.
          * Used as a pseudonymous identifier for logging.
          * @deprecated This identifier is poorly initialized and has
diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java
index f8bf45b..7e9aba0 100644
--- a/core/java/android/security/IKeystoreService.java
+++ b/core/java/android/security/IKeystoreService.java
@@ -478,6 +478,59 @@
                 }
                 return _result;
             }
+
+            public int reset_uid(int uid) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt(uid);
+                    mRemote.transact(Stub.TRANSACTION_reset_uid, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            public int sync_uid(int srcUid, int dstUid) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt(srcUid);
+                    _data.writeInt(dstUid);
+                    mRemote.transact(Stub.TRANSACTION_sync_uid, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            public int password_uid(String password, int uid) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(password);
+                    _data.writeInt(uid);
+                    mRemote.transact(Stub.TRANSACTION_password_uid, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
         }
 
         private static final String DESCRIPTOR = "android.security.keystore";
@@ -505,6 +558,9 @@
         static final int TRANSACTION_duplicate = IBinder.FIRST_CALL_TRANSACTION + 20;
         static final int TRANSACTION_is_hardware_backed = IBinder.FIRST_CALL_TRANSACTION + 21;
         static final int TRANSACTION_clear_uid = IBinder.FIRST_CALL_TRANSACTION + 22;
+        static final int TRANSACTION_reset_uid = IBinder.FIRST_CALL_TRANSACTION + 23;
+        static final int TRANSACTION_sync_uid = IBinder.FIRST_CALL_TRANSACTION + 24;
+        static final int TRANSACTION_password_uid = IBinder.FIRST_CALL_TRANSACTION + 25;
 
         /**
          * Cast an IBinder object into an IKeystoreService interface, generating
@@ -597,4 +653,10 @@
     public int is_hardware_backed(String string) throws RemoteException;
 
     public int clear_uid(long uid) throws RemoteException;
+
+    public int reset_uid(int uid) throws RemoteException;
+
+    public int sync_uid(int sourceUid, int targetUid) throws RemoteException;
+
+    public int password_uid(String password, int uid) throws RemoteException;
 }
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index a544b2d..cb0bcf2 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -81,6 +81,33 @@
      * This does not change the interruption filter, only the effects. **/
     public static final int HINT_HOST_DISABLE_EFFECTS = 1;
 
+    /**
+     * The full trim of the StatusBarNotification including all its features.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int TRIM_FULL = 0;
+
+    /**
+     * A light trim of the StatusBarNotification excluding the following features:
+     *
+     * <ol>
+     *     <li>{@link Notification#tickerView tickerView}</li>
+     *     <li>{@link Notification#contentView contentView}</li>
+     *     <li>{@link Notification#largeIcon largeIcon}</li>
+     *     <li>{@link Notification#bigContentView bigContentView}</li>
+     *     <li>{@link Notification#headsUpContentView headsUpContentView}</li>
+     *     <li>{@link Notification#EXTRA_LARGE_ICON extras[EXTRA_LARGE_ICON]}</li>
+     *     <li>{@link Notification#EXTRA_LARGE_ICON_BIG extras[EXTRA_LARGE_ICON_BIG]}</li>
+     *     <li>{@link Notification#EXTRA_PICTURE extras[EXTRA_PICTURE]}</li>
+     * </ol>
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int TRIM_LIGHT = 1;
+
     private INotificationListenerWrapper mWrapper = null;
     private RankingMap mRankingMap;
 
@@ -314,13 +341,53 @@
     }
 
     /**
+     * Sets the notification trim that will be received via {@link #onNotificationPosted}.
+     *
+     * <p>
+     * Setting a trim other than {@link #TRIM_FULL} enables listeners that don't need access to the
+     * full notification features right away to reduce their memory footprint. Full notifications
+     * can be requested on-demand via {@link #getActiveNotifications(int)}.
+     *
+     * <p>
+     * Set to {@link #TRIM_FULL} initially.
+     *
+     * @hide
+     *
+     * @param trim trim of the notifications to be passed via {@link #onNotificationPosted}.
+     *             See <code>TRIM_*</code> constants.
+     */
+    @SystemApi
+    public final void setOnNotificationPostedTrim(int trim) {
+        if (!isBound()) return;
+        try {
+            getNotificationInterface().setOnNotificationPostedTrimFromListener(mWrapper, trim);
+        } catch (RemoteException ex) {
+            Log.v(TAG, "Unable to contact notification manager", ex);
+        }
+    }
+
+    /**
      * Request the list of outstanding notifications (that is, those that are visible to the
      * current user). Useful when you don't know what's already been posted.
      *
      * @return An array of active notifications, sorted in natural order.
      */
     public StatusBarNotification[] getActiveNotifications() {
-        return getActiveNotifications(null);
+        return getActiveNotifications(null, TRIM_FULL);
+    }
+
+    /**
+     * Request the list of outstanding notifications (that is, those that are visible to the
+     * current user). Useful when you don't know what's already been posted.
+     *
+     * @hide
+     *
+     * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants.
+     * @return An array of active notifications, sorted in natural order.
+     */
+    @SystemApi
+    public StatusBarNotification[] getActiveNotifications(int trim) {
+        return getActiveNotifications(null, trim);
     }
 
     /**
@@ -328,14 +395,33 @@
      * notifications but didn't want to retain the bits, and now need to go back and extract
      * more data out of those notifications.
      *
+     * @param keys the keys of the notifications to request
      * @return An array of notifications corresponding to the requested keys, in the
      * same order as the key list.
      */
     public StatusBarNotification[] getActiveNotifications(String[] keys) {
-        if (!isBound()) return null;
+        return getActiveNotifications(keys, TRIM_FULL);
+    }
+
+    /**
+     * Request one or more notifications by key. Useful if you have been keeping track of
+     * notifications but didn't want to retain the bits, and now need to go back and extract
+     * more data out of those notifications.
+     *
+     * @hide
+     *
+     * @param keys the keys of the notifications to request
+     * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants.
+     * @return An array of notifications corresponding to the requested keys, in the
+     * same order as the key list.
+     */
+    @SystemApi
+    public StatusBarNotification[] getActiveNotifications(String[] keys, int trim) {
+        if (!isBound())
+            return null;
         try {
-            ParceledListSlice<StatusBarNotification> parceledList =
-                    getNotificationInterface().getActiveNotificationsFromListener(mWrapper, keys);
+            ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface()
+                    .getActiveNotificationsFromListener(mWrapper, keys, trim);
             List<StatusBarNotification> list = parceledList.getList();
 
             int N = list.size();
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 2095773..519bc28 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.Activity;
 import android.content.Intent;
 import android.hardware.soundtrigger.IRecognitionStatusCallback;
 import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
@@ -84,20 +85,31 @@
     private static final int STATE_NOT_READY = 0;
 
     // Keyphrase management actions. Used in getManageIntent() ----//
-    /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(value = {
                 MANAGE_ACTION_ENROLL,
                 MANAGE_ACTION_RE_ENROLL,
                 MANAGE_ACTION_UN_ENROLL
             })
-    public @interface ManageActions {}
+    private @interface ManageActions {}
 
-    /** Indicates that we need to enroll. */
+    /**
+     * Indicates that we need to enroll.
+     *
+     * @hide
+     */
     public static final int MANAGE_ACTION_ENROLL = 0;
-    /** Indicates that we need to re-enroll. */
+    /**
+     * Indicates that we need to re-enroll.
+     *
+     * @hide
+     */
     public static final int MANAGE_ACTION_RE_ENROLL = 1;
-    /** Indicates that we need to un-enroll. */
+    /**
+     * Indicates that we need to un-enroll.
+     *
+     * @hide
+     */
     public static final int MANAGE_ACTION_UN_ENROLL = 2;
 
     //-- Flags for startRecognition    ----//
@@ -111,7 +123,11 @@
             })
     public @interface RecognitionFlags {}
 
-    /** Empty flag for {@link #startRecognition(int)}. */
+    /**
+     * Empty flag for {@link #startRecognition(int)}.
+     *
+     * @hide
+     */
     public static final int RECOGNITION_FLAG_NONE = 0;
     /**
      * Recognition flag for {@link #startRecognition(int)} that indicates
@@ -264,7 +280,7 @@
     /**
      * Callbacks for always-on hotword detection.
      */
-    public interface Callback {
+    public static abstract class Callback {
         /**
          * Called when the hotword availability changes.
          * This indicates a change in the availability of recognition for the given keyphrase.
@@ -278,7 +294,7 @@
          * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNENROLLED
          * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_ENROLLED
          */
-        void onAvailabilityChanged(int status);
+        public abstract void onAvailabilityChanged(int status);
         /**
          * Called when the keyphrase is spoken.
          * This implicitly stops listening for the keyphrase once it's detected.
@@ -289,23 +305,23 @@
          *        This may contain the trigger audio, if requested when calling
          *        {@link AlwaysOnHotwordDetector#startRecognition(int)}.
          */
-        void onDetected(@NonNull EventPayload eventPayload);
+        public abstract void onDetected(@NonNull EventPayload eventPayload);
         /**
          * Called when the detection fails due to an error.
          */
-        void onError();
+        public abstract void onError();
         /**
          * Called when the recognition is paused temporarily for some reason.
          * This is an informational callback, and the clients shouldn't be doing anything here
          * except showing an indication on their UI if they have to.
          */
-        void onRecognitionPaused();
+        public abstract void onRecognitionPaused();
         /**
          * Called when the recognition is resumed after it was temporarily paused.
          * This is an informational callback, and the clients shouldn't be doing anything here
          * except showing an indication on their UI if they have to.
          */
-        void onRecognitionResumed();
+        public abstract void onRecognitionResumed();
     }
 
     /**
@@ -372,10 +388,10 @@
     /**
      * Starts recognition for the associated keyphrase.
      *
+     * @see #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
+     * @see #RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS
+     *
      * @param recognitionFlags The flags to control the recognition properties.
-     *        The allowed flags are {@link #RECOGNITION_FLAG_NONE},
-     *        {@link #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO} and
-     *        {@link #RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS}.
      * @return Indicates whether the call succeeded or not.
      * @throws UnsupportedOperationException if the recognition isn't supported.
      *         Callers should only call this method after a supported state callback on
@@ -430,12 +446,13 @@
     }
 
     /**
-     * Gets an intent to manage the associated keyphrase.
+     * Creates an intent to start the enrollment for the associated keyphrase.
+     * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
+     * Starting re-enrollment is only valid if the keyphrase is un-enrolled,
+     * i.e. {@link #STATE_KEYPHRASE_UNENROLLED},
+     * otherwise {@link #createIntentToReEnroll()} should be preferred.
      *
-     * @param action The manage action that needs to be performed.
-     *        One of {@link #MANAGE_ACTION_ENROLL}, {@link #MANAGE_ACTION_RE_ENROLL} or
-     *        {@link #MANAGE_ACTION_UN_ENROLL}.
-     * @return An {@link Intent} to manage the given keyphrase.
+     * @return An {@link Intent} to start enrollment for the given keyphrase.
      * @throws UnsupportedOperationException if managing they keyphrase isn't supported.
      *         Callers should only call this method after a supported state callback on
      *         {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
@@ -443,10 +460,52 @@
      *         This may happen if another detector has been instantiated or the
      *         {@link VoiceInteractionService} hosting this detector has been shut down.
      */
-    public Intent getManageIntent(@ManageActions int action) {
-        if (DBG) Slog.d(TAG, "getManageIntent(" + action + ")");
+    public Intent createIntentToEnroll() {
+        if (DBG) Slog.d(TAG, "createIntentToEnroll");
         synchronized (mLock) {
-            return getManageIntentLocked(action);
+            return getManageIntentLocked(MANAGE_ACTION_ENROLL);
+        }
+    }
+
+    /**
+     * Creates an intent to start the un-enrollment for the associated keyphrase.
+     * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
+     * Starting re-enrollment is only valid if the keyphrase is already enrolled,
+     * i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error.
+     *
+     * @return An {@link Intent} to start un-enrollment for the given keyphrase.
+     * @throws UnsupportedOperationException if managing they keyphrase isn't supported.
+     *         Callers should only call this method after a supported state callback on
+     *         {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
+     * @throws IllegalStateException if the detector is in an invalid state.
+     *         This may happen if another detector has been instantiated or the
+     *         {@link VoiceInteractionService} hosting this detector has been shut down.
+     */
+    public Intent createIntentToUnEnroll() {
+        if (DBG) Slog.d(TAG, "createIntentToUnEnroll");
+        synchronized (mLock) {
+            return getManageIntentLocked(MANAGE_ACTION_UN_ENROLL);
+        }
+    }
+
+    /**
+     * Creates an intent to start the re-enrollment for the associated keyphrase.
+     * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
+     * Starting re-enrollment is only valid if the keyphrase is already enrolled,
+     * i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error.
+     *
+     * @return An {@link Intent} to start re-enrollment for the given keyphrase.
+     * @throws UnsupportedOperationException if managing they keyphrase isn't supported.
+     *         Callers should only call this method after a supported state callback on
+     *         {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
+     * @throws IllegalStateException if the detector is in an invalid state.
+     *         This may happen if another detector has been instantiated or the
+     *         {@link VoiceInteractionService} hosting this detector has been shut down.
+     */
+    public Intent createIntentToReEnroll() {
+        if (DBG) Slog.d(TAG, "createIntentToReEnroll");
+        synchronized (mLock) {
+            return getManageIntentLocked(MANAGE_ACTION_RE_ENROLL);
         }
     }
 
@@ -462,12 +521,6 @@
                     "Managing the given keyphrase is not supported");
         }
 
-        if (action != MANAGE_ACTION_ENROLL
-                && action != MANAGE_ACTION_RE_ENROLL
-                && action != MANAGE_ACTION_UN_ENROLL) {
-            throw new IllegalArgumentException("Invalid action specified " + action);
-        }
-
         return mKeyphraseEnrollmentInfo.getManageKeyphraseIntent(action, mText, mLocale);
     }
 
diff --git a/core/java/android/text/format/TimeFormatter.java b/core/java/android/text/format/TimeFormatter.java
index ec79b36..3a63805 100644
--- a/core/java/android/text/format/TimeFormatter.java
+++ b/core/java/android/text/format/TimeFormatter.java
@@ -22,8 +22,7 @@
 
 import android.content.res.Resources;
 
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
+import java.nio.CharBuffer;
 import java.util.Formatter;
 import java.util.Locale;
 import java.util.TimeZone;
@@ -31,15 +30,13 @@
 import libcore.util.ZoneInfo;
 
 /**
- * Formatting logic for {@link Time}. Contains a port of Bionic's broken strftime_tz to Java. The
- * main issue with this implementation is the treatment of characters as ASCII, despite returning
- * localized (UTF-16) strings from the LocaleData.
+ * Formatting logic for {@link Time}. Contains a port of Bionic's broken strftime_tz to Java.
  *
  * <p>This class is not thread safe.
  */
 class TimeFormatter {
-    // An arbitrary value outside the range representable by a byte / ASCII character code.
-    private static final int FORCE_LOWER_CASE = 0x100;
+    // An arbitrary value outside the range representable by a char.
+    private static final int FORCE_LOWER_CASE = -1;
 
     private static final int SECSPERMIN = 60;
     private static final int MINSPERHOUR = 60;
@@ -62,10 +59,9 @@
     private final String dateTimeFormat;
     private final String timeOnlyFormat;
     private final String dateOnlyFormat;
-    private final Locale locale;
 
     private StringBuilder outputBuilder;
-    private Formatter outputFormatter;
+    private Formatter numberFormatter;
 
     public TimeFormatter() {
         synchronized (TimeFormatter.class) {
@@ -84,7 +80,6 @@
             this.dateTimeFormat = sDateTimeFormat;
             this.timeOnlyFormat = sTimeOnlyFormat;
             this.dateOnlyFormat = sDateOnlyFormat;
-            this.locale = locale;
             localeData = sLocaleData;
         }
     }
@@ -97,19 +92,21 @@
             StringBuilder stringBuilder = new StringBuilder();
 
             outputBuilder = stringBuilder;
-            outputFormatter = new Formatter(stringBuilder, locale);
+            // This uses the US locale because number localization is handled separately (see below)
+            // and locale sensitive strings are output directly using outputBuilder.
+            numberFormatter = new Formatter(stringBuilder, Locale.US);
 
             formatInternal(pattern, wallTime, zoneInfo);
             String result = stringBuilder.toString();
             // This behavior is the source of a bug since some formats are defined as being
-            // in ASCII. Generally localization is very broken.
+            // in ASCII and not localized.
             if (localeData.zeroDigit != '0') {
                 result = localizeDigits(result);
             }
             return result;
         } finally {
             outputBuilder = null;
-            outputFormatter = null;
+            numberFormatter = null;
         }
     }
 
@@ -132,38 +129,30 @@
      * {@link #outputBuilder}.
      */
     private void formatInternal(String pattern, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) {
-        // Convert to ASCII bytes to be compatible with old implementation behavior.
-        byte[] bytes = pattern.getBytes(StandardCharsets.US_ASCII);
-        if (bytes.length == 0) {
-            return;
-        }
-
-        ByteBuffer formatBuffer = ByteBuffer.wrap(bytes);
+        CharBuffer formatBuffer = CharBuffer.wrap(pattern);
         while (formatBuffer.remaining() > 0) {
-            boolean outputCurrentByte = true;
-            char currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position()));
-            if (currentByteAsChar == '%') {
-                outputCurrentByte = handleToken(formatBuffer, wallTime, zoneInfo);
+            boolean outputCurrentChar = true;
+            char currentChar = formatBuffer.get(formatBuffer.position());
+            if (currentChar == '%') {
+                outputCurrentChar = handleToken(formatBuffer, wallTime, zoneInfo);
             }
-            if (outputCurrentByte) {
-                currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position()));
-                outputBuilder.append(currentByteAsChar);
+            if (outputCurrentChar) {
+                outputBuilder.append(formatBuffer.get(formatBuffer.position()));
             }
-
             formatBuffer.position(formatBuffer.position() + 1);
         }
     }
 
-    private boolean handleToken(ByteBuffer formatBuffer, ZoneInfo.WallTime wallTime,
+    private boolean handleToken(CharBuffer formatBuffer, ZoneInfo.WallTime wallTime,
             ZoneInfo zoneInfo) {
 
-        // The byte at formatBuffer.position() is expected to be '%' at this point.
+        // The char at formatBuffer.position() is expected to be '%' at this point.
         int modifier = 0;
         while (formatBuffer.remaining() > 1) {
-            // Increment the position then get the new current byte.
+            // Increment the position then get the new current char.
             formatBuffer.position(formatBuffer.position() + 1);
-            char currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position()));
-            switch (currentByteAsChar) {
+            char currentChar = formatBuffer.get(formatBuffer.position());
+            switch (currentChar) {
                 case 'A':
                     modifyAndAppend((wallTime.getWeekDay() < 0
                                     || wallTime.getWeekDay() >= DAYSPERWEEK)
@@ -206,7 +195,7 @@
                     formatInternal("%m/%d/%y", wallTime, zoneInfo);
                     return false;
                 case 'd':
-                    outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
                             wallTime.getMonthDay());
                     return false;
                 case 'E':
@@ -218,46 +207,46 @@
                 case '0':
                 case '^':
                 case '#':
-                    modifier = currentByteAsChar;
+                    modifier = currentChar;
                     continue;
                 case 'e':
-                    outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
+                    numberFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
                             wallTime.getMonthDay());
                     return false;
                 case 'F':
                     formatInternal("%Y-%m-%d", wallTime, zoneInfo);
                     return false;
                 case 'H':
-                    outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
                             wallTime.getHour());
                     return false;
                 case 'I':
                     int hour = (wallTime.getHour() % 12 != 0) ? (wallTime.getHour() % 12) : 12;
-                    outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), hour);
+                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), hour);
                     return false;
                 case 'j':
                     int yearDay = wallTime.getYearDay() + 1;
-                    outputFormatter.format(getFormat(modifier, "%03d", "%3d", "%d", "%03d"),
+                    numberFormatter.format(getFormat(modifier, "%03d", "%3d", "%d", "%03d"),
                             yearDay);
                     return false;
                 case 'k':
-                    outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
+                    numberFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
                             wallTime.getHour());
                     return false;
                 case 'l':
                     int n2 = (wallTime.getHour() % 12 != 0) ? (wallTime.getHour() % 12) : 12;
-                    outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"), n2);
+                    numberFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"), n2);
                     return false;
                 case 'M':
-                    outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
                             wallTime.getMinute());
                     return false;
                 case 'm':
-                    outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
                             wallTime.getMonth() + 1);
                     return false;
                 case 'n':
-                    modifyAndAppend("\n", modifier);
+                    outputBuilder.append('\n');
                     return false;
                 case 'p':
                     modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1]
@@ -274,27 +263,27 @@
                     formatInternal("%I:%M:%S %p", wallTime, zoneInfo);
                     return false;
                 case 'S':
-                    outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
                             wallTime.getSecond());
                     return false;
                 case 's':
                     int timeInSeconds = wallTime.mktime(zoneInfo);
-                    modifyAndAppend(Integer.toString(timeInSeconds), modifier);
+                    outputBuilder.append(Integer.toString(timeInSeconds));
                     return false;
                 case 'T':
                     formatInternal("%H:%M:%S", wallTime, zoneInfo);
                     return false;
                 case 't':
-                    modifyAndAppend("\t", modifier);
+                    outputBuilder.append('\t');
                     return false;
                 case 'U':
-                    outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
                             (wallTime.getYearDay() + DAYSPERWEEK - wallTime.getWeekDay())
                                     / DAYSPERWEEK);
                     return false;
                 case 'u':
                     int day = (wallTime.getWeekDay() == 0) ? DAYSPERWEEK : wallTime.getWeekDay();
-                    outputFormatter.format("%d", day);
+                    numberFormatter.format("%d", day);
                     return false;
                 case 'V':   /* ISO 8601 week number */
                 case 'G':   /* ISO 8601 year (four digits) */
@@ -326,9 +315,9 @@
                         --year;
                         yday += isLeap(year) ? DAYSPERLYEAR : DAYSPERNYEAR;
                     }
-                    if (currentByteAsChar == 'V') {
-                        outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), w);
-                    } else if (currentByteAsChar == 'g') {
+                    if (currentChar == 'V') {
+                        numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), w);
+                    } else if (currentChar == 'g') {
                         outputYear(year, false, true, modifier);
                     } else {
                         outputYear(year, true, true, modifier);
@@ -342,10 +331,10 @@
                     int n = (wallTime.getYearDay() + DAYSPERWEEK - (
                                     wallTime.getWeekDay() != 0 ? (wallTime.getWeekDay() - 1)
                                             : (DAYSPERWEEK - 1))) / DAYSPERWEEK;
-                    outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
+                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
                     return false;
                 case 'w':
-                    outputFormatter.format("%d", wallTime.getWeekDay());
+                    numberFormatter.format("%d", wallTime.getWeekDay());
                     return false;
                 case 'X':
                     formatInternal(timeOnlyFormat, wallTime, zoneInfo);
@@ -371,17 +360,17 @@
                         return false;
                     }
                     int diff = wallTime.getGmtOffset();
-                    String sign;
+                    char sign;
                     if (diff < 0) {
-                        sign = "-";
+                        sign = '-';
                         diff = -diff;
                     } else {
-                        sign = "+";
+                        sign = '+';
                     }
-                    modifyAndAppend(sign, modifier);
+                    outputBuilder.append(sign);
                     diff /= SECSPERMIN;
                     diff = (diff / MINSPERHOUR) * 100 + (diff % MINSPERHOUR);
-                    outputFormatter.format(getFormat(modifier, "%04d", "%4d", "%d", "%04d"), diff);
+                    numberFormatter.format(getFormat(modifier, "%04d", "%4d", "%d", "%04d"), diff);
                     return false;
                 }
                 case '+':
@@ -422,7 +411,6 @@
                 break;
             default:
                 outputBuilder.append(str);
-
         }
     }
 
@@ -443,14 +431,14 @@
         }
         if (outputTop) {
             if (lead == 0 && trail < 0) {
-                modifyAndAppend("-0", modifier);
+                outputBuilder.append("-0");
             } else {
-                outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), lead);
+                numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), lead);
             }
         }
         if (outputBottom) {
             int n = ((trail < 0) ? -trail : trail);
-            outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
+            numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
         }
     }
 
@@ -472,24 +460,24 @@
     }
 
     /**
-     * A broken implementation of {@link Character#isUpperCase(char)} that assumes ASCII in order to
-     * be compatible with the old native implementation.
+     * A broken implementation of {@link Character#isUpperCase(char)} that assumes ASCII codes in
+     * order to be compatible with the old native implementation.
      */
     private static boolean brokenIsUpper(char toCheck) {
         return toCheck >= 'A' && toCheck <= 'Z';
     }
 
     /**
-     * A broken implementation of {@link Character#isLowerCase(char)} that assumes ASCII in order to
-     * be compatible with the old native implementation.
+     * A broken implementation of {@link Character#isLowerCase(char)} that assumes ASCII codes in
+     * order to be compatible with the old native implementation.
      */
     private static boolean brokenIsLower(char toCheck) {
         return toCheck >= 'a' && toCheck <= 'z';
     }
 
     /**
-     * A broken implementation of {@link Character#toLowerCase(char)} that assumes ASCII in order to
-     * be compatible with the old native implementation.
+     * A broken implementation of {@link Character#toLowerCase(char)} that assumes ASCII codes in
+     * order to be compatible with the old native implementation.
      */
     private static char brokenToLower(char input) {
         if (input >= 'A' && input <= 'Z') {
@@ -499,8 +487,8 @@
     }
 
     /**
-     * A broken implementation of {@link Character#toUpperCase(char)} that assumes ASCII in order to
-     * be compatible with the old native implementation.
+     * A broken implementation of {@link Character#toUpperCase(char)} that assumes ASCII codes in
+     * order to be compatible with the old native implementation.
      */
     private static char brokenToUpper(char input) {
         if (input >= 'a' && input <= 'z') {
@@ -509,11 +497,4 @@
         return input;
     }
 
-    /**
-     * Safely convert a byte containing an ASCII character to a char, even for character codes
-     * > 127.
-     */
-    private static char convertToChar(byte b) {
-        return (char) (b & 0xFF);
-    }
 }
diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java
index ebb1a5c..eb17429 100644
--- a/core/java/android/transition/ChangeBounds.java
+++ b/core/java/android/transition/ChangeBounds.java
@@ -31,6 +31,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.util.IntProperty;
 import android.util.Property;
 import android.view.View;
 import android.view.ViewGroup;
@@ -185,25 +186,36 @@
             }
             if (numChanges > 0) {
                 if (!mResizeClip) {
-                    if (startLeft != endLeft) view.setLeft(startLeft);
-                    if (startTop != endTop) view.setTop(startTop);
-                    if (startRight != endRight) view.setRight(startRight);
-                    if (startBottom != endBottom) view.setBottom(startBottom);
-                    ObjectAnimator topLeftAnimator = null;
-                    if (startLeft != endLeft || startTop != endTop) {
-                        Path topLeftPath = getPathMotion().getPath(startLeft, startTop,
-                                endLeft, endTop);
-                        topLeftAnimator = ObjectAnimator.ofInt(view, "left", "top", topLeftPath);
+                    Animator anim;
+                    if (startWidth == endWidth && startHeight == endHeight) {
+                        view.offsetLeftAndRight(startLeft - view.getLeft());
+                        view.offsetTopAndBottom(startTop - view.getTop());
+                        Path positionPath = getPathMotion().getPath(0, 0, endLeft - startLeft,
+                                endTop - startTop);
+                        anim = ObjectAnimator.ofInt(view, new HorizontalOffsetProperty(),
+                                new VerticalOffsetProperty(), positionPath);
+                    } else {
+                        if (startLeft != endLeft) view.setLeft(startLeft);
+                        if (startTop != endTop) view.setTop(startTop);
+                        if (startRight != endRight) view.setRight(startRight);
+                        if (startBottom != endBottom) view.setBottom(startBottom);
+                        ObjectAnimator topLeftAnimator = null;
+                        if (startLeft != endLeft || startTop != endTop) {
+                            Path topLeftPath = getPathMotion().getPath(startLeft, startTop,
+                                    endLeft, endTop);
+                            topLeftAnimator = ObjectAnimator
+                                    .ofInt(view, "left", "top", topLeftPath);
+                        }
+                        ObjectAnimator bottomRightAnimator = null;
+                        if (startRight != endRight || startBottom != endBottom) {
+                            Path bottomRightPath = getPathMotion().getPath(startRight, startBottom,
+                                    endRight, endBottom);
+                            bottomRightAnimator = ObjectAnimator.ofInt(view, "right", "bottom",
+                                    bottomRightPath);
+                        }
+                        anim = TransitionUtils.mergeAnimators(topLeftAnimator,
+                                bottomRightAnimator);
                     }
-                    ObjectAnimator bottomRightAnimator = null;
-                    if (startRight != endRight || startBottom != endBottom) {
-                        Path bottomRightPath = getPathMotion().getPath(startRight, startBottom,
-                                endRight, endBottom);
-                        bottomRightAnimator = ObjectAnimator.ofInt(view, "right", "bottom",
-                                bottomRightPath);
-                    }
-                    Animator anim = TransitionUtils.mergeAnimators(topLeftAnimator,
-                            bottomRightAnimator);
                     if (view.getParent() instanceof ViewGroup) {
                         final ViewGroup parent = (ViewGroup) view.getParent();
                         parent.suppressLayout(true);
@@ -341,4 +353,48 @@
         }
         return null;
     }
+
+    private abstract static class OffsetProperty extends IntProperty<View> {
+        int mPreviousValue;
+
+        public OffsetProperty(String name) {
+            super(name);
+        }
+
+        @Override
+        public void setValue(View view, int value) {
+            int offset = value - mPreviousValue;
+            offsetBy(view, offset);
+            mPreviousValue = value;
+        }
+
+        @Override
+        public Integer get(View object) {
+            return null;
+        }
+
+        protected abstract void offsetBy(View view, int by);
+    }
+
+    private static class HorizontalOffsetProperty extends OffsetProperty {
+        public HorizontalOffsetProperty() {
+            super("offsetLeftAndRight");
+        }
+
+        @Override
+        protected void offsetBy(View view, int by) {
+            view.offsetLeftAndRight(by);
+        }
+    }
+
+    private static class VerticalOffsetProperty extends OffsetProperty {
+        public VerticalOffsetProperty() {
+            super("offsetTopAndBottom");
+        }
+
+        @Override
+        protected void offsetBy(View view, int by) {
+            view.offsetTopAndBottom(by);
+        }
+    }
 }
diff --git a/core/java/android/util/Spline.java b/core/java/android/util/Spline.java
index ed027eb..41a2e5d 100644
--- a/core/java/android/util/Spline.java
+++ b/core/java/android/util/Spline.java
@@ -20,15 +20,34 @@
  * Performs spline interpolation given a set of control points.
  * @hide
  */
-public final class Spline {
-    private final float[] mX;
-    private final float[] mY;
-    private final float[] mM;
+public abstract class Spline {
 
-    private Spline(float[] x, float[] y, float[] m) {
-        mX = x;
-        mY = y;
-        mM = m;
+    /**
+     * Interpolates the value of Y = f(X) for given X.
+     * Clamps X to the domain of the spline.
+     *
+     * @param x The X value.
+     * @return The interpolated Y = f(X) value.
+     */
+    public abstract float interpolate(float x);
+
+    /**
+     * Creates an appropriate spline based on the properties of the control points.
+     *
+     * If the control points are monotonic then the resulting spline will preserve that and
+     * otherwise optimize for error bounds.
+     */
+    public static Spline createSpline(float[] x, float[] y) {
+        if (!isStrictlyIncreasing(x)) {
+            throw new IllegalArgumentException("The control points must all have strictly "
+                    + "increasing X values.");
+        }
+
+        if (isMonotonic(y)) {
+            return createMonotoneCubicSpline(x, y);
+        } else {
+            return createLinearSpline(x, y);
+        }
     }
 
     /**
@@ -50,107 +69,229 @@
      * @throws IllegalArgumentException if the control points are not monotonic.
      */
     public static Spline createMonotoneCubicSpline(float[] x, float[] y) {
-        if (x == null || y == null || x.length != y.length || x.length < 2) {
-            throw new IllegalArgumentException("There must be at least two control "
-                    + "points and the arrays must be of equal length.");
-        }
-
-        final int n = x.length;
-        float[] d = new float[n - 1]; // could optimize this out
-        float[] m = new float[n];
-
-        // Compute slopes of secant lines between successive points.
-        for (int i = 0; i < n - 1; i++) {
-            float h = x[i + 1] - x[i];
-            if (h <= 0f) {
-                throw new IllegalArgumentException("The control points must all "
-                        + "have strictly increasing X values.");
-            }
-            d[i] = (y[i + 1] - y[i]) / h;
-        }
-
-        // Initialize the tangents as the average of the secants.
-        m[0] = d[0];
-        for (int i = 1; i < n - 1; i++) {
-            m[i] = (d[i - 1] + d[i]) * 0.5f;
-        }
-        m[n - 1] = d[n - 2];
-
-        // Update the tangents to preserve monotonicity.
-        for (int i = 0; i < n - 1; i++) {
-            if (d[i] == 0f) { // successive Y values are equal
-                m[i] = 0f;
-                m[i + 1] = 0f;
-            } else {
-                float a = m[i] / d[i];
-                float b = m[i + 1] / d[i];
-                if (a < 0f || b < 0f) {
-                    throw new IllegalArgumentException("The control points must have "
-                            + "monotonic Y values.");
-                }
-                float h = FloatMath.hypot(a, b);
-                if (h > 9f) {
-                    float t = 3f / h;
-                    m[i] = t * a * d[i];
-                    m[i + 1] = t * b * d[i];
-                }
-            }
-        }
-        return new Spline(x, y, m);
+        return new MonotoneCubicSpline(x, y);
     }
 
     /**
-     * Interpolates the value of Y = f(X) for given X.
-     * Clamps X to the domain of the spline.
+     * Creates a linear spline from a given set of control points.
      *
-     * @param x The X value.
-     * @return The interpolated Y = f(X) value.
+     * Like a monotone cubic spline, the interpolated curve will be monotonic if the control points
+     * are monotonic.
+     *
+     * @param x The X component of the control points, strictly increasing.
+     * @param y The Y component of the control points.
+     * @return
+     *
+     * @throws IllegalArgumentException if the X or Y arrays are null, have
+     * different lengths or have fewer than 2 values.
+     * @throws IllegalArgumentException if the X components of the control points are not strictly
+     * increasing.
      */
-    public float interpolate(float x) {
-        // Handle the boundary cases.
-        final int n = mX.length;
-        if (Float.isNaN(x)) {
-            return x;
-        }
-        if (x <= mX[0]) {
-            return mY[0];
-        }
-        if (x >= mX[n - 1]) {
-            return mY[n - 1];
-        }
-
-        // Find the index 'i' of the last point with smaller X.
-        // We know this will be within the spline due to the boundary tests.
-        int i = 0;
-        while (x >= mX[i + 1]) {
-            i += 1;
-            if (x == mX[i]) {
-                return mY[i];
-            }
-        }
-
-        // Perform cubic Hermite spline interpolation.
-        float h = mX[i + 1] - mX[i];
-        float t = (x - mX[i]) / h;
-        return (mY[i] * (1 + 2 * t) + h * mM[i] * t) * (1 - t) * (1 - t)
-                + (mY[i + 1] * (3 - 2 * t) + h * mM[i + 1] * (t - 1)) * t * t;
+    public static Spline createLinearSpline(float[] x, float[] y) {
+        return new LinearSpline(x, y);
     }
 
-    // For debugging.
-    @Override
-    public String toString() {
-        StringBuilder str = new StringBuilder();
-        final int n = mX.length;
-        str.append("[");
-        for (int i = 0; i < n; i++) {
-            if (i != 0) {
-                str.append(", ");
-            }
-            str.append("(").append(mX[i]);
-            str.append(", ").append(mY[i]);
-            str.append(": ").append(mM[i]).append(")");
+    private static boolean isStrictlyIncreasing(float[] x) {
+        if (x == null || x.length < 2) {
+            throw new IllegalArgumentException("There must be at least two control points.");
         }
-        str.append("]");
-        return str.toString();
+        float prev = x[0];
+        for (int i = 1; i < x.length; i++) {
+            float curr = x[i];
+            if (curr <= prev) {
+                return false;
+            }
+            prev = curr;
+        }
+        return true;
+    }
+
+    private static boolean isMonotonic(float[] x) {
+        if (x == null || x.length < 2) {
+            throw new IllegalArgumentException("There must be at least two control points.");
+        }
+        float prev = x[0];
+        for (int i = 1; i < x.length; i++) {
+            float curr = x[i];
+            if (curr < prev) {
+                return false;
+            }
+            prev = curr;
+        }
+        return true;
+    }
+
+    public static class MonotoneCubicSpline extends Spline {
+        private float[] mX;
+        private float[] mY;
+        private float[] mM;
+
+        public MonotoneCubicSpline(float[] x, float[] y) {
+            if (x == null || y == null || x.length != y.length || x.length < 2) {
+                throw new IllegalArgumentException("There must be at least two control "
+                        + "points and the arrays must be of equal length.");
+            }
+
+            final int n = x.length;
+            float[] d = new float[n - 1]; // could optimize this out
+            float[] m = new float[n];
+
+            // Compute slopes of secant lines between successive points.
+            for (int i = 0; i < n - 1; i++) {
+                float h = x[i + 1] - x[i];
+                if (h <= 0f) {
+                    throw new IllegalArgumentException("The control points must all "
+                            + "have strictly increasing X values.");
+                }
+                d[i] = (y[i + 1] - y[i]) / h;
+            }
+
+            // Initialize the tangents as the average of the secants.
+            m[0] = d[0];
+            for (int i = 1; i < n - 1; i++) {
+                m[i] = (d[i - 1] + d[i]) * 0.5f;
+            }
+            m[n - 1] = d[n - 2];
+
+            // Update the tangents to preserve monotonicity.
+            for (int i = 0; i < n - 1; i++) {
+                if (d[i] == 0f) { // successive Y values are equal
+                    m[i] = 0f;
+                    m[i + 1] = 0f;
+                } else {
+                    float a = m[i] / d[i];
+                    float b = m[i + 1] / d[i];
+                    if (a < 0f || b < 0f) {
+                        throw new IllegalArgumentException("The control points must have "
+                                + "monotonic Y values.");
+                    }
+                    float h = FloatMath.hypot(a, b);
+                    if (h > 9f) {
+                        float t = 3f / h;
+                        m[i] = t * a * d[i];
+                        m[i + 1] = t * b * d[i];
+                    }
+                }
+            }
+
+            mX = x;
+            mY = y;
+            mM = m;
+        }
+
+        @Override
+        public float interpolate(float x) {
+            // Handle the boundary cases.
+            final int n = mX.length;
+            if (Float.isNaN(x)) {
+                return x;
+            }
+            if (x <= mX[0]) {
+                return mY[0];
+            }
+            if (x >= mX[n - 1]) {
+                return mY[n - 1];
+            }
+
+            // Find the index 'i' of the last point with smaller X.
+            // We know this will be within the spline due to the boundary tests.
+            int i = 0;
+            while (x >= mX[i + 1]) {
+                i += 1;
+                if (x == mX[i]) {
+                    return mY[i];
+                }
+            }
+
+            // Perform cubic Hermite spline interpolation.
+            float h = mX[i + 1] - mX[i];
+            float t = (x - mX[i]) / h;
+            return (mY[i] * (1 + 2 * t) + h * mM[i] * t) * (1 - t) * (1 - t)
+                    + (mY[i + 1] * (3 - 2 * t) + h * mM[i + 1] * (t - 1)) * t * t;
+        }
+
+        // For debugging.
+        @Override
+        public String toString() {
+            StringBuilder str = new StringBuilder();
+            final int n = mX.length;
+            str.append("MonotoneCubicSpline{[");
+            for (int i = 0; i < n; i++) {
+                if (i != 0) {
+                    str.append(", ");
+                }
+                str.append("(").append(mX[i]);
+                str.append(", ").append(mY[i]);
+                str.append(": ").append(mM[i]).append(")");
+            }
+            str.append("]}");
+            return str.toString();
+        }
+    }
+
+    public static class LinearSpline extends Spline {
+        private final float[] mX;
+        private final float[] mY;
+        private final float[] mM;
+
+        public LinearSpline(float[] x, float[] y) {
+            if (x == null || y == null || x.length != y.length || x.length < 2) {
+                throw new IllegalArgumentException("There must be at least two control "
+                        + "points and the arrays must be of equal length.");
+            }
+            final int N = x.length;
+            mM = new float[N-1];
+            for (int i = 0; i < N-1; i++) {
+                mM[i] = (y[i+1] - y[i]) / (x[i+1] - x[i]);
+            }
+            mX = x;
+            mY = y;
+        }
+
+        @Override
+        public float interpolate(float x) {
+            // Handle the boundary cases.
+            final int n = mX.length;
+            if (Float.isNaN(x)) {
+                return x;
+            }
+            if (x <= mX[0]) {
+                return mY[0];
+            }
+            if (x >= mX[n - 1]) {
+                return mY[n - 1];
+            }
+
+            // Find the index 'i' of the last point with smaller X.
+            // We know this will be within the spline due to the boundary tests.
+            int i = 0;
+            while (x >= mX[i + 1]) {
+                i += 1;
+                if (x == mX[i]) {
+                    return mY[i];
+                }
+            }
+            return mY[i] + mM[i] * (x - mX[i]);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder str = new StringBuilder();
+            final int n = mX.length;
+            str.append("LinearSpline{[");
+            for (int i = 0; i < n; i++) {
+                if (i != 0) {
+                    str.append(", ");
+                }
+                str.append("(").append(mX[i]);
+                str.append(", ").append(mY[i]);
+                if (i < n-1) {
+                    str.append(": ").append(mM[i]);
+                }
+                str.append(")");
+            }
+            str.append("]}");
+            return str.toString();
+        }
     }
 }
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 413ea04..fa4a13a 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -87,8 +87,11 @@
     private float mFinalValue;
     private TimeInterpolator mInterpolator;
 
-    private boolean mStarted = false;
-    private boolean mFinished = false;
+    private static final int STATE_PREPARE = 0;
+    private static final int STATE_DELAYED = 1;
+    private static final int STATE_RUNNING = 2;
+    private static final int STATE_FINISHED = 3;
+    private int mState = STATE_PREPARE;
 
     private long mUnscaledDuration = 300;
     private long mUnscaledStartDelay = 0;
@@ -142,7 +145,7 @@
     }
 
     private void checkMutable() {
-        if (mStarted) {
+        if (mState != STATE_PREPARE) {
             throw new IllegalStateException("Animator has already started, cannot change it now!");
         }
     }
@@ -170,11 +173,11 @@
             throw new IllegalStateException("Missing target!");
         }
 
-        if (mStarted) {
+        if (mState != STATE_PREPARE) {
             throw new IllegalStateException("Already started!");
         }
 
-        mStarted = true;
+        mState = STATE_DELAYED;
         applyInterpolator();
 
         if (mStartDelay <= 0 || !mUiThreadHandlesDelay) {
@@ -186,6 +189,7 @@
     }
 
     private void doStart() {
+        mState = STATE_RUNNING;
         nStart(mNativePtr.get(), this);
 
         // Alpha is a special snowflake that has the canonical value stored
@@ -197,11 +201,7 @@
             mViewTarget.mTransformationInfo.mAlpha = mFinalValue;
         }
 
-        final ArrayList<AnimatorListener> listeners = cloneListeners();
-        final int numListeners = listeners == null ? 0 : listeners.size();
-        for (int i = 0; i < numListeners; i++) {
-            listeners.get(i).onAnimationStart(this);
-        }
+        notifyStartListeners();
 
         if (mViewTarget != null) {
             // Kick off a frame to start the process
@@ -209,10 +209,21 @@
         }
     }
 
+    private void notifyStartListeners() {
+        final ArrayList<AnimatorListener> listeners = cloneListeners();
+        final int numListeners = listeners == null ? 0 : listeners.size();
+        for (int i = 0; i < numListeners; i++) {
+            listeners.get(i).onAnimationStart(this);
+        }
+    }
+
     @Override
     public void cancel() {
-        if (!mFinished) {
-            getHelper().removeDelayedAnimation(this);
+        if (mState != STATE_FINISHED) {
+            if (mState == STATE_DELAYED) {
+                getHelper().removeDelayedAnimation(this);
+                notifyStartListeners();
+            }
             nEnd(mNativePtr.get());
 
             final ArrayList<AnimatorListener> listeners = cloneListeners();
@@ -220,12 +231,17 @@
             for (int i = 0; i < numListeners; i++) {
                 listeners.get(i).onAnimationCancel(this);
             }
+
+            if (mViewTarget != null) {
+                // Kick off a frame to flush the state change
+                mViewTarget.invalidateViewProperty(true, false);
+            }
         }
     }
 
     @Override
     public void end() {
-        if (!mFinished) {
+        if (mState != STATE_FINISHED) {
             nEnd(mNativePtr.get());
         }
     }
@@ -299,12 +315,12 @@
 
     @Override
     public boolean isRunning() {
-        return mStarted && !mFinished;
+        return mState == STATE_DELAYED || mState == STATE_RUNNING;
     }
 
     @Override
     public boolean isStarted() {
-        return mStarted;
+        return mState != STATE_PREPARE;
     }
 
     @Override
@@ -319,7 +335,11 @@
     }
 
     protected void onFinished() {
-        mFinished = true;
+        if (mState == STATE_DELAYED) {
+            getHelper().removeDelayedAnimation(this);
+            notifyStartListeners();
+        }
+        mState = STATE_FINISHED;
 
         final ArrayList<AnimatorListener> listeners = cloneListeners();
         final int numListeners = listeners == null ? 0 : listeners.size();
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 602f955..56bdb9b 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -176,7 +176,7 @@
                 RemoteViewsAdapter adapter;
                 final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
                 if ((adapter = mAdapter.get()) != null) {
-                    mgr.unbindRemoteViewsService(context.getPackageName(), appWidgetId, intent);
+                    mgr.unbindRemoteViewsService(context.getOpPackageName(), appWidgetId, intent);
                 } else {
                     Slog.w(TAG, "unbind: adapter was null");
                 }
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 818efaa..ece8aa4 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -244,6 +244,16 @@
         // Set the default context, since setPopupTheme() may be a no-op.
         mPopupContext = mContext;
         setPopupTheme(a.getResourceId(R.styleable.Toolbar_popupTheme, 0));
+
+        final Drawable navIcon = a.getDrawable(R.styleable.Toolbar_navigationIcon);
+        if (navIcon != null) {
+            setNavigationIcon(navIcon);
+            final CharSequence navDesc = a.getText(
+                    R.styleable.Toolbar_navigationContentDescription);
+            if (!TextUtils.isEmpty(navDesc)) {
+                setNavigationContentDescription(navDesc);
+            }
+        }
         a.recycle();
     }
 
@@ -669,6 +679,8 @@
      * as screen readers or tooltips.
      *
      * @return The navigation button's content description
+     *
+     * @attr ref android.R.styleable#Toolbar_navigationContentDescription
      */
     @Nullable
     public CharSequence getNavigationContentDescription() {
@@ -682,6 +694,8 @@
      *
      * @param resId Resource ID of a content description string to set, or 0 to
      *              clear the description
+     *
+     * @attr ref android.R.styleable#Toolbar_navigationContentDescription
      */
     public void setNavigationContentDescription(int resId) {
         setNavigationContentDescription(resId != 0 ? getContext().getText(resId) : null);
@@ -694,6 +708,8 @@
      *
      * @param description Content description to set, or <code>null</code> to
      *                    clear the content description
+     *
+     * @attr ref android.R.styleable#Toolbar_navigationContentDescription
      */
     public void setNavigationContentDescription(@Nullable CharSequence description) {
         if (!TextUtils.isEmpty(description)) {
@@ -715,6 +731,8 @@
      * tooltips.</p>
      *
      * @param resId Resource ID of a drawable to set
+     *
+     * @attr ref android.R.styleable#Toolbar_navigationIcon
      */
     public void setNavigationIcon(int resId) {
         setNavigationIcon(getContext().getDrawable(resId));
@@ -731,6 +749,8 @@
      * tooltips.</p>
      *
      * @param icon Drawable to set, may be null to clear the icon
+     *
+     * @attr ref android.R.styleable#Toolbar_navigationIcon
      */
     public void setNavigationIcon(@Nullable Drawable icon) {
         if (icon != null) {
@@ -751,6 +771,8 @@
      * Return the current drawable used as the navigation icon.
      *
      * @return The navigation icon drawable
+     *
+     * @attr ref android.R.styleable#Toolbar_navigationIcon
      */
     @Nullable
     public Drawable getNavigationIcon() {
@@ -1316,6 +1338,8 @@
             final View bottomChild = layoutSubtitle ? mSubtitleTextView : mTitleTextView;
             final LayoutParams toplp = (LayoutParams) topChild.getLayoutParams();
             final LayoutParams bottomlp = (LayoutParams) bottomChild.getLayoutParams();
+            final boolean titleHasWidth = layoutTitle && mTitleTextView.getMeasuredWidth() > 0
+                    || layoutSubtitle && mSubtitleTextView.getMeasuredWidth() > 0;
 
             switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) {
                 case Gravity.TOP:
@@ -1343,7 +1367,7 @@
                     break;
             }
             if (isRtl) {
-                final int rd = mTitleMarginStart - collapsingMargins[1];
+                final int rd = (titleHasWidth ? mTitleMarginStart : 0) - collapsingMargins[1];
                 right -= Math.max(0, rd);
                 collapsingMargins[1] = Math.max(0, -rd);
                 int titleRight = right;
@@ -1366,9 +1390,11 @@
                     subtitleRight = subtitleRight - mTitleMarginEnd;
                     titleTop = subtitleBottom + lp.bottomMargin;
                 }
-                right = Math.min(titleRight, subtitleRight);
+                if (titleHasWidth) {
+                    right = Math.min(titleRight, subtitleRight);
+                }
             } else {
-                final int ld = mTitleMarginStart - collapsingMargins[0];
+                final int ld = (titleHasWidth ? mTitleMarginStart : 0) - collapsingMargins[0];
                 left += Math.max(0, ld);
                 collapsingMargins[0] = Math.max(0, -ld);
                 int titleLeft = left;
@@ -1391,7 +1417,9 @@
                     subtitleLeft = subtitleRight + mTitleMarginEnd;
                     titleTop = subtitleBottom + lp.bottomMargin;
                 }
-                left = Math.max(titleLeft, subtitleLeft);
+                if (titleHasWidth) {
+                    left = Math.max(titleLeft, subtitleLeft);
+                }
             }
         }
 
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 61b4567..b6e7353 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -23,7 +23,6 @@
 import android.os.AsyncTask;
 import android.provider.Settings;
 import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.Slog;
 import android.widget.AbsListView;
 import android.widget.GridView;
@@ -73,6 +72,7 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -100,7 +100,7 @@
     private boolean mResolvingHome = false;
 
     private UsageStatsManager mUsm;
-    private ArrayMap<String, UsageStats> mStats;
+    private Map<String, UsageStats> mStats;
     private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14;
 
     private boolean mRegistered;
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index 2967938..ba236f39 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -191,6 +191,20 @@
     public boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode) {
         if (DEBUG) Log.v(TAG, "requestUpdateCursorAnchorInfo " + cursorUpdateMode);
 
+        // It is possible that any other bit is used as a valid flag in a future release.
+        // We should reject the entire request in such a case.
+        final int KNOWN_FLAGS_MASK = InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE |
+                InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_MONITOR;
+        final int unknownFlags = cursorUpdateMode & ~KNOWN_FLAGS_MASK;
+        if (unknownFlags != 0) {
+            if (DEBUG) {
+                Log.d(TAG, "Rejecting requestUpdateCursorAnchorInfo due to unknown flags." +
+                        " cursorUpdateMode=" + cursorUpdateMode +
+                        " unknownFlags=" + unknownFlags);
+            }
+            return false;
+        }
+
         if (mIMM == null) {
             // In this case, TYPE_CURSOR_ANCHOR_INFO is not handled.
             // TODO: Return some notification code rather than false to indicate method that
diff --git a/core/res/res/color/btn_default_material_dark.xml b/core/res/res/color/btn_default_material_dark.xml
index 7c904cd..9be1417 100644
--- a/core/res/res/color/btn_default_material_dark.xml
+++ b/core/res/res/color/btn_default_material_dark.xml
@@ -16,7 +16,7 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false"
-        android:alpha="@dimen/disabled_alpha_material"
-        android:color="@color/button_material_dark"/>
+          android:alpha="@dimen/disabled_alpha_material_dark"
+          android:color="@color/button_material_dark"/>
     <item android:color="@color/button_material_dark"/>
 </selector>
diff --git a/core/res/res/color/btn_default_material_light.xml b/core/res/res/color/btn_default_material_light.xml
index 738f9ad..af5afe6 100644
--- a/core/res/res/color/btn_default_material_light.xml
+++ b/core/res/res/color/btn_default_material_light.xml
@@ -16,7 +16,7 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false"
-        android:alpha="@dimen/disabled_alpha_material"
-        android:color="@color/button_material_light"/>
+          android:alpha="@dimen/disabled_alpha_material_light"
+          android:color="@color/button_material_light"/>
     <item android:color="@color/button_material_light"/>
 </selector>
diff --git a/core/res/res/color/primary_text_disable_only_material_dark.xml b/core/res/res/color/primary_text_disable_only_material_dark.xml
index cdae790..a6296c96 100644
--- a/core/res/res/color/primary_text_disable_only_material_dark.xml
+++ b/core/res/res/color/primary_text_disable_only_material_dark.xml
@@ -16,7 +16,7 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false"
-        android:alpha="@dimen/disabled_alpha_material"
-        android:color="@color/bright_foreground_material_dark"/>
+          android:alpha="@dimen/disabled_alpha_material_dark"
+          android:color="@color/bright_foreground_material_dark"/>
     <item android:color="@color/bright_foreground_material_dark"/>
 </selector>
diff --git a/core/res/res/color/primary_text_disable_only_material_light.xml b/core/res/res/color/primary_text_disable_only_material_light.xml
index 0bf14d0..b781844 100644
--- a/core/res/res/color/primary_text_disable_only_material_light.xml
+++ b/core/res/res/color/primary_text_disable_only_material_light.xml
@@ -16,7 +16,7 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false"
-        android:alpha="@dimen/disabled_alpha_material"
-        android:color="@color/bright_foreground_material_light"/>
+          android:alpha="@dimen/disabled_alpha_material_light"
+          android:color="@color/bright_foreground_material_light"/>
     <item android:color="@color/bright_foreground_material_light"/>
 </selector>
diff --git a/core/res/res/color/primary_text_material_dark.xml b/core/res/res/color/primary_text_material_dark.xml
index 6ad837b..690f0ec 100644
--- a/core/res/res/color/primary_text_material_dark.xml
+++ b/core/res/res/color/primary_text_material_dark.xml
@@ -16,7 +16,7 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false"
-        android:alpha="@dimen/disabled_alpha_material"
-        android:color="@color/primary_text_default_material_dark"/>
+          android:alpha="@dimen/disabled_alpha_material_dark"
+          android:color="@color/primary_text_default_material_dark"/>
     <item android:color="@color/primary_text_default_material_dark"/>
 </selector>
diff --git a/core/res/res/color/primary_text_material_light.xml b/core/res/res/color/primary_text_material_light.xml
index 4c19e12..634ad94 100644
--- a/core/res/res/color/primary_text_material_light.xml
+++ b/core/res/res/color/primary_text_material_light.xml
@@ -16,7 +16,7 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false"
-        android:alpha="@dimen/disabled_alpha_material"
-        android:color="@color/primary_text_default_material_light"/>
+          android:alpha="@dimen/disabled_alpha_material_light"
+          android:color="@color/primary_text_default_material_light"/>
     <item android:color="@color/primary_text_default_material_light"/>
 </selector>
diff --git a/packages/DocumentsUI/res/drawable/grid_protect_background.xml b/core/res/res/color/secondary_text_material_dark.xml
similarity index 68%
copy from packages/DocumentsUI/res/drawable/grid_protect_background.xml
copy to core/res/res/color/secondary_text_material_dark.xml
index 2e7aadd..5bddf96 100644
--- a/packages/DocumentsUI/res/drawable/grid_protect_background.xml
+++ b/core/res/res/color/secondary_text_material_dark.xml
@@ -1,12 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- Copyright (C) 2008 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
-
+  
           http://www.apache.org/licenses/LICENSE-2.0
-
+  
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,10 +15,8 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false">
-        <color android:color="#88000000" />
-    </item>
-    <item>
-        <color android:color="#88252525" />
-    </item>
+    <item android:state_enabled="false"
+          android:alpha="@dimen/disabled_alpha_material_dark"
+          android:color="@color/secondary_text_default_material_dark"/>
+    <item android:color="@color/secondary_text_default_material_dark"/>
 </selector>
diff --git a/packages/DocumentsUI/res/drawable/grid_protect_background.xml b/core/res/res/color/secondary_text_material_light.xml
similarity index 68%
copy from packages/DocumentsUI/res/drawable/grid_protect_background.xml
copy to core/res/res/color/secondary_text_material_light.xml
index 2e7aadd..3c354cb 100644
--- a/packages/DocumentsUI/res/drawable/grid_protect_background.xml
+++ b/core/res/res/color/secondary_text_material_light.xml
@@ -1,12 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- Copyright (C) 2008 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
-
+  
           http://www.apache.org/licenses/LICENSE-2.0
-
+  
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,10 +15,8 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false">
-        <color android:color="#88000000" />
-    </item>
-    <item>
-        <color android:color="#88252525" />
-    </item>
+    <item android:state_enabled="false"
+          android:alpha="@dimen/disabled_alpha_material_light"
+          android:color="@color/secondary_text_default_material_light"/>
+    <item android:color="@color/secondary_text_default_material_light"/>
 </selector>
diff --git a/core/res/res/drawable/progress_horizontal_material.xml b/core/res/res/drawable/progress_horizontal_material.xml
index 7a1079c..6b64337 100644
--- a/core/res/res/drawable/progress_horizontal_material.xml
+++ b/core/res/res/drawable/progress_horizontal_material.xml
@@ -18,13 +18,13 @@
     <item android:id="@id/background">
         <nine-patch android:src="@drawable/progress_mtrl_alpha"
             android:tint="?attr/colorControlNormal"
-            android:alpha="0.5" />
+            android:alpha="?attr/disabledAlpha" />
     </item>
     <item android:id="@id/secondaryProgress">
         <scale android:scaleWidth="100%">
             <nine-patch android:src="@drawable/progress_mtrl_alpha"
                 android:tint="?attr/colorControlActivated"
-                android:alpha="0.5" />
+                android:alpha="?attr/disabledAlpha" />
         </scale>
     </item>
     <item android:id="@id/progress">
diff --git a/core/res/res/layout/action_menu_item_layout.xml b/core/res/res/layout/action_menu_item_layout.xml
index 04d1f7b..6961f8d 100644
--- a/core/res/res/layout/action_menu_item_layout.xml
+++ b/core/res/res/layout/action_menu_item_layout.xml
@@ -25,5 +25,4 @@
     android:paddingStart="8dip"
     android:paddingEnd="8dip"
     android:textAppearance="?attr/actionMenuTextAppearance"
-    android:textColor="?attr/actionMenuTextColor"
-    style="?android:attr/actionButtonStyle" />
+    style="?attr/actionButtonStyle" />
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index 3595033..ef916ed1 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -53,8 +53,8 @@
                 android:visibility="gone"
                 />
             <ImageView android:id="@+id/profile_badge_large_template"
-                android:layout_width="20dp"
-                android:layout_height="20dp"
+                android:layout_width="@dimen/notification_badge_size"
+                android:layout_height="@dimen/notification_badge_size"
                 android:layout_weight="0"
                 android:layout_marginStart="4dp"
                 android:scaleType="fitCenter"
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index d601d4a3..3415814 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -39,11 +39,12 @@
         <include layout="@layout/notification_template_part_line2" />
         <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
+            android:layout_height="0dp"
             android:layout_marginEnd="8dp"
             android:layout_marginBottom="10dp"
             android:orientation="horizontal"
             android:gravity="top"
+            android:layout_weight="1"
             >
             <TextView android:id="@+id/big_text"
                 android:textAppearance="@style/TextAppearance.Material.Notification"
@@ -54,8 +55,8 @@
                 android:visibility="gone"
                 />
             <ImageView android:id="@+id/profile_badge_large_template"
-                android:layout_width="20dp"
-                android:layout_height="20dp"
+                android:layout_width="@dimen/notification_badge_size"
+                android:layout_height="@dimen/notification_badge_size"
                 android:layout_weight="0"
                 android:layout_marginStart="4dp"
                 android:scaleType="fitCenter"
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index 2bd9f81..2382d18 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -37,95 +37,27 @@
         >
         <include layout="@layout/notification_template_part_line1" />
         <include layout="@layout/notification_template_part_line2" />
+
+        <!-- We can't have another vertical linear layout here with weight != 0 so this forces us to
+             put the badge on the first line. -->
         <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_height="0dp"
             android:orientation="horizontal"
-            android:gravity="top"
             >
-            <LinearLayout
+            <TextView android:id="@+id/inbox_text0"
+                android:textAppearance="@style/TextAppearance.Material.Notification"
                 android:layout_width="0dp"
-                android:layout_weight="1"
                 android:layout_height="wrap_content"
-                android:orientation="vertical"
-                >
-                <TextView android:id="@+id/inbox_text0"
-                    android:textAppearance="@style/TextAppearance.Material.Notification"
-                    android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:singleLine="true"
-                    android:ellipsize="end"
-                    android:visibility="gone"
-                    android:layout_weight="1"
-                    />
-                <TextView android:id="@+id/inbox_text1"
-                    android:textAppearance="@style/TextAppearance.Material.Notification"
-                    android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:singleLine="true"
-                    android:ellipsize="end"
-                    android:visibility="gone"
-                    android:layout_weight="1"
-                    />
-                <TextView android:id="@+id/inbox_text2"
-                    android:textAppearance="@style/TextAppearance.Material.Notification"
-                    android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:singleLine="true"
-                    android:ellipsize="end"
-                    android:visibility="gone"
-                    android:layout_weight="1"
-                    />
-                <TextView android:id="@+id/inbox_text3"
-                    android:textAppearance="@style/TextAppearance.Material.Notification"
-                    android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:singleLine="true"
-                    android:ellipsize="end"
-                    android:visibility="gone"
-                    android:layout_weight="1"
-                    />
-                <TextView android:id="@+id/inbox_text4"
-                    android:textAppearance="@style/TextAppearance.Material.Notification"
-                    android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:singleLine="true"
-                    android:ellipsize="end"
-                    android:visibility="gone"
-                    android:layout_weight="1"
-                    />
-                <TextView android:id="@+id/inbox_text5"
-                    android:textAppearance="@style/TextAppearance.Material.Notification"
-                    android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:singleLine="true"
-                    android:ellipsize="end"
-                    android:visibility="gone"
-                    android:layout_weight="1"
-                    />
-                <TextView android:id="@+id/inbox_text6"
-                    android:textAppearance="@style/TextAppearance.Material.Notification"
-                    android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:singleLine="true"
-                    android:ellipsize="end"
-                    android:visibility="gone"
-                    android:layout_weight="1"
-                    />
-                <TextView android:id="@+id/inbox_more"
-                    android:textAppearance="@style/TextAppearance.Material.Notification"
-                    android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:singleLine="true"
-                    android:ellipsize="end"
-                    android:visibility="gone"
-                    android:layout_weight="1"
-                    android:text="@android:string/ellipsis"
-                    />
-            </LinearLayout>
+                android:singleLine="true"
+                android:ellipsize="end"
+                android:visibility="gone"
+                android:layout_weight="1"
+                />
             <ImageView android:id="@+id/profile_badge_large_template"
-                android:layout_width="20dp"
-                android:layout_height="20dp"
+                android:layout_width="@dimen/notification_badge_size"
+                android:layout_height="@dimen/notification_badge_size"
                 android:layout_weight="0"
                 android:layout_marginStart="4dp"
                 android:layout_marginEnd="8dp"
@@ -133,6 +65,77 @@
                 android:visibility="gone"
                 />
         </LinearLayout>
+        <TextView android:id="@+id/inbox_text1"
+            android:textAppearance="@style/TextAppearance.Material.Notification"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_marginEnd="8dp"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:visibility="gone"
+            android:layout_weight="1"
+            />
+        <TextView android:id="@+id/inbox_text2"
+            android:textAppearance="@style/TextAppearance.Material.Notification"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_marginEnd="8dp"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:visibility="gone"
+            android:layout_weight="1"
+            />
+        <TextView android:id="@+id/inbox_text3"
+            android:textAppearance="@style/TextAppearance.Material.Notification"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_marginEnd="8dp"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:visibility="gone"
+            android:layout_weight="1"
+            />
+        <TextView android:id="@+id/inbox_text4"
+            android:textAppearance="@style/TextAppearance.Material.Notification"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_marginEnd="8dp"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:visibility="gone"
+            android:layout_weight="1"
+            />
+        <TextView android:id="@+id/inbox_text5"
+            android:textAppearance="@style/TextAppearance.Material.Notification"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_marginEnd="8dp"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:visibility="gone"
+            android:layout_weight="1"
+            />
+        <TextView android:id="@+id/inbox_text6"
+            android:textAppearance="@style/TextAppearance.Material.Notification"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_marginEnd="8dp"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:visibility="gone"
+            android:layout_weight="1"
+            />
+        <TextView android:id="@+id/inbox_more"
+            android:textAppearance="@style/TextAppearance.Material.Notification"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_marginEnd="8dp"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:visibility="gone"
+            android:layout_weight="1"
+            android:text="@android:string/ellipsis"
+            />
         <FrameLayout
             android:id="@+id/inbox_end_pad"
             android:layout_width="match_parent"
diff --git a/core/res/res/layout/notification_template_part_line2.xml b/core/res/res/layout/notification_template_part_line2.xml
index 28d2bef..7e99c5e 100644
--- a/core/res/res/layout/notification_template_part_line2.xml
+++ b/core/res/res/layout/notification_template_part_line2.xml
@@ -20,8 +20,6 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginEnd="8dp"
-        android:visibility="gone"
-        android:layout_weight="0"
         android:orientation="horizontal"
         android:gravity="center_vertical"
         >
@@ -39,8 +37,8 @@
             android:layout_weight="1"
         />
         <ImageView android:id="@+id/profile_badge_line2"
-            android:layout_width="20dp"
-            android:layout_height="20dp"
+            android:layout_width="@dimen/notification_badge_size"
+            android:layout_height="@dimen/notification_badge_size"
             android:layout_weight="0"
             android:layout_marginStart="4dp"
             android:scaleType="fitCenter"
diff --git a/core/res/res/layout/notification_template_part_line3.xml b/core/res/res/layout/notification_template_part_line3.xml
index 56de5c9..6c043a0 100644
--- a/core/res/res/layout/notification_template_part_line3.xml
+++ b/core/res/res/layout/notification_template_part_line3.xml
@@ -44,8 +44,8 @@
         android:paddingStart="8dp"
         />
     <ImageView android:id="@+id/profile_badge_line3"
-        android:layout_width="20dp"
-        android:layout_height="20dp"
+        android:layout_width="@dimen/notification_badge_size"
+        android:layout_height="@dimen/notification_badge_size"
         android:layout_gravity="center"
         android:layout_weight="0"
         android:layout_marginStart="4dp"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9da116a..a798d2e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7318,6 +7318,12 @@
         <!-- Reference to a theme that should be used to inflate popups
              shown by widgets in the toolbar. -->
         <attr name="popupTheme" format="reference" />
+        <!-- Icon drawable to use for the navigation button located at
+             the start of the toolbar. -->
+        <attr name="navigationIcon" format="reference" />
+        <!-- Text to set as the content description for the navigation button
+             located at the start of the toolbar. -->
+        <attr name="navigationContentDescription" format="string" />
     </declare-styleable>
 
     <declare-styleable name="Toolbar_LayoutParams">
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index c9292eb..d8e14a0 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -58,19 +58,14 @@
     <!-- Text & foreground colors -->
     <eat-comment />
 
-    <!-- Black 87% -->
     <color name="primary_text_default_material_light">#de000000</color>
-    <!-- Black 54% -->
-    <color name="secondary_text_material_light">#8a000000</color>
-    <!-- Black 54% (TODO: same as secondary?) -->
-    <color name="tertiary_text_material_light">#8a000000</color>
+    <color name="secondary_text_default_material_light">#8a000000</color>
 
-    <!-- White 87% -->
-    <color name="primary_text_default_material_dark">#deffffff</color>
-    <!-- White 38% -->
-    <color name="secondary_text_material_dark">#61ffffff</color>
-    <!-- White 38% (TODO: same as secondary?) -->
-    <color name="tertiary_text_material_dark">#61ffffff</color>
+    <color name="primary_text_default_material_dark">#ffffffff</color>
+    <color name="secondary_text_default_material_dark">#b3ffffff</color>
+
+    <item name="disabled_alpha_material_light" format="float" type="dimen">0.26</item>
+    <item name="disabled_alpha_material_dark" format="float" type="dimen">0.30</item>
 
     <!-- Primary & accent colors -->
     <eat-comment />
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 4e3abb9..77b451f 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -236,6 +236,9 @@
     <!-- Padding for notification icon when drawn with circle around it -->
     <dimen name="notification_large_icon_circle_padding">11dp</dimen>
 
+    <!-- Size of the profile badge for notifications -->
+    <dimen name="notification_badge_size">16dp</dimen>
+
     <!-- Keyguard dimensions -->
     <!-- TEMP -->
     <dimen name="kg_security_panel_height">600dp</dimen>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index f5c9299..5f7f0ed 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -77,8 +77,5 @@
     <!-- Default rounded corner for controls -->
     <dimen name="control_corner_material">2dp</dimen>
 
-    <!-- Default alpha value for disabled elements. -->
-    <item name="disabled_alpha_material" format="float" type="dimen">0.26</item>
-
-    <dimen name="alert_dialog_padding_material">12dp</dimen>
+    <dimen name="alert_dialog_padding_material">18dp</dimen>
 </resources>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 4c59f73..bd24f3e 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -87,4 +87,6 @@
   <item type="id" name="transitionPosition" />
   <item type="id" name="transitionTransform" />
   <item type="id" name="parentMatrix" />
+  <item type="id" name="statusBarBackground" />
+  <item type="id" name="navigationBarBackground" />
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3b49bed..5e76a87 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2280,12 +2280,16 @@
   <public type="attr" name="reparentWithOverlay" />
   <public type="attr" name="ambientShadowAlpha" />
   <public type="attr" name="spotShadowAlpha" />
+  <public type="attr" name="navigationIcon" />
+  <public type="attr" name="navigationContentDescription" />
 
   <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
 
   <public-padding type="id" name="l_resource_pad" end="0x01020040" />
 
   <public type="id" name="mask" />
+  <public type="id" name="statusBarBackground" />
+  <public type="id" name="navigationBarBackground" />
 
   <public-padding type="style" name="l_resource_pad" end="0x01030200" />
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d8c29b0..622a01a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -357,6 +357,7 @@
   <java-symbol type="dimen" name="notification_top_pad_large_text" />
   <java-symbol type="dimen" name="notification_top_pad_large_text_narrow" />
   <java-symbol type="dimen" name="notification_large_icon_circle_padding" />
+  <java-symbol type="dimen" name="notification_badge_size" />
   <java-symbol type="dimen" name="immersive_mode_cling_width" />
   <java-symbol type="dimen" name="circular_display_mask_offset" />
 
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 89fac13..2296a12 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -46,7 +46,7 @@
         <item name="colorForegroundInverse">@color/bright_foreground_material_light</item>
         <item name="colorBackground">@color/background_material_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_dark</item>
-        <item name="disabledAlpha">@dimen/disabled_alpha_material</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_material_dark</item>
         <item name="backgroundDimAmount">0.6</item>
 
         <!-- Text styles -->
@@ -58,8 +58,8 @@
         <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_material_dark</item>
         <item name="textColorSecondary">@color/secondary_text_material_dark</item>
         <item name="textColorSecondaryInverse">@color/secondary_text_material_light</item>
-        <item name="textColorTertiary">@color/tertiary_text_material_dark</item>
-        <item name="textColorTertiaryInverse">@color/tertiary_text_material_light</item>
+        <item name="textColorTertiary">@color/secondary_text_material_dark</item>
+        <item name="textColorTertiaryInverse">@color/secondary_text_material_light</item>
         <item name="textColorHint">@color/hint_foreground_material_dark</item>
         <item name="textColorHintInverse">@color/hint_foreground_material_light</item>
         <item name="textColorHighlight">@color/highlighted_text_material_dark</item>
@@ -387,7 +387,7 @@
         <item name="colorForegroundInverse">@color/bright_foreground_material_dark</item>
         <item name="colorBackground">@color/background_material_light</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_light</item>
-        <item name="disabledAlpha">@dimen/disabled_alpha_material</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_material_light</item>
         <item name="backgroundDimAmount">0.6</item>
 
         <!-- Text styles -->
@@ -398,8 +398,8 @@
         <item name="textColorPrimaryInverse">@color/primary_text_material_dark</item>
         <item name="textColorSecondary">@color/secondary_text_material_light</item>
         <item name="textColorSecondaryInverse">@color/secondary_text_material_dark</item>
-        <item name="textColorTertiary">@color/tertiary_text_material_light</item>
-        <item name="textColorTertiaryInverse">@color/tertiary_text_material_dark</item>
+        <item name="textColorTertiary">@color/secondary_text_material_light</item>
+        <item name="textColorTertiaryInverse">@color/secondary_text_material_dark</item>
         <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_material_light</item>
         <item name="textColorPrimaryInverseDisableOnly">@color/primary_text_disable_only_material_dark</item>
         <item name="textColorHint">@color/hint_foreground_material_light</item>
@@ -750,8 +750,8 @@
         <item name="textColorPrimaryInverse">@color/primary_text_material_dark</item>
         <item name="textColorSecondary">@color/secondary_text_material_light</item>
         <item name="textColorSecondaryInverse">@color/secondary_text_material_dark</item>
-        <item name="textColorTertiary">@color/tertiary_text_material_light</item>
-        <item name="textColorTertiaryInverse">@color/tertiary_text_material_dark</item>
+        <item name="textColorTertiary">@color/secondary_text_material_light</item>
+        <item name="textColorTertiaryInverse">@color/secondary_text_material_dark</item>
         <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_material_light</item>
         <item name="textColorPrimaryInverseDisableOnly">@color/primary_text_disable_only_material_dark</item>
         <item name="textColorHint">@color/hint_foreground_material_light</item>
@@ -790,8 +790,8 @@
         <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_material_dark</item>
         <item name="textColorSecondary">@color/secondary_text_material_dark</item>
         <item name="textColorSecondaryInverse">@color/secondary_text_material_light</item>
-        <item name="textColorTertiary">@color/tertiary_text_material_dark</item>
-        <item name="textColorTertiaryInverse">@color/tertiary_text_material_light</item>
+        <item name="textColorTertiary">@color/secondary_text_material_dark</item>
+        <item name="textColorTertiaryInverse">@color/secondary_text_material_light</item>
         <item name="textColorHint">@color/hint_foreground_material_dark</item>
         <item name="textColorHintInverse">@color/hint_foreground_material_light</item>
         <item name="textColorHighlight">@color/highlighted_text_material_dark</item>
diff --git a/docs/html/guide/topics/admin/device-admin.jd b/docs/html/guide/topics/admin/device-admin.jd
index ee6b814..bed4b4d 100644
--- a/docs/html/guide/topics/admin/device-admin.jd
+++ b/docs/html/guide/topics/admin/device-admin.jd
@@ -28,12 +28,6 @@
       <li>{@link android.app.admin.DevicePolicyManager}</li>
       <li>{@link android.app.admin.DeviceAdminInfo}</li>
     </ol>
-    <h2>Related samples</h2>
-    <ol>
-      <li><a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html">
-DeviceAdminSample</a></li>
-</ol>
 </div>
 </div>
 
@@ -232,18 +226,12 @@
 
 <h2 id="sample">Sample Application</h2>
 
-<p>The examples used in this document are based on the <a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html">
-Device Administration API
-sample</a>, which is included in the SDK samples. For information on downloading and
-installing the SDK samples, see <a
-href="{@docRoot}resources/samples/get.html">
-Getting the Samples</a>. Here is the  <a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html">
-complete code</a> for
-the sample. </p>
-<p>The
-sample application offers a demo of device admin features. It presents users
+<p>The examples used in this document are based on the Device Administration API
+sample, which is included in the SDK samples (available through the
+Android SDK Manager) and located on your system as 
+<code>&lt;sdk_root&gt;/ApiDemos/app/src/main/java/com/example/android/apis/app/DeviceAdminSample.java</code>.</p>
+
+<p>The sample application offers a demo of device admin features. It presents users
 with a user interface that lets them enable the device admin application. Once
 they've enabled the application, they can use the buttons in the user interface
 to do the following:</p>
@@ -676,7 +664,8 @@
 <p>You can also programmatically tell the device to lock immediately:</p>
 <pre>
 DevicePolicyManager mDPM;
-mDPM.lockNow();</pre>
+mDPM.lockNow();
+</pre>
 
 
 
@@ -692,12 +681,12 @@
 <pre>
 DevicePolicyManager mDPM;
 mDPM.wipeData(0);</pre>
-<p>The {@link android.app.admin.DevicePolicyManager#wipeData wipeData()} method takes as its parameter a bit mask of
-additional options. Currently the value must be 0. </p>
+<p>The {@link android.app.admin.DevicePolicyManager#wipeData wipeData()} method takes as its
+  parameter a bit mask of additional options. Currently the value must be 0. </p>
 
 <h4>Disable camera</h4>
 <p>Beginning with Android 4.0, you can disable the camera. Note that this doesn't have to be a permanent disabling. The camera can be enabled/disabled dynamically based on context, time, and so on. </p>
-<p>You control whether the camera is disabled by using the 
+<p>You control whether the camera is disabled by using the
 {@link android.app.admin.DevicePolicyManager#setCameraDisabled(android.content.ComponentName, boolean) setCameraDisabled()} method. For example, this snippet sets the camera to be enabled or disabled based on a checkbox setting:</p>
 
 <pre>private CheckBoxPreference mDisableCameraCheckbox;
@@ -708,8 +697,8 @@
 </pre>
 
 
-<h4 id=storage">Storage encryption</h4>
-<p>Beginning with Android 3.0, you can use the 
+<h4 id="storage">Storage encryption</h4>
+<p>Beginning with Android 3.0, you can use the
 {@link android.app.admin.DevicePolicyManager#setStorageEncryption(android.content.ComponentName,boolean) setStorageEncryption()} 
 method to set a policy requiring encryption of the storage area, where supported.</p>
 
@@ -722,5 +711,5 @@
 mDPM.setStorageEncryption(mDeviceAdminSample, true);
 </pre>
 <p>
-See the <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html"> Device Administration API sample</a> for a complete
-example of how to enable storage encryption.</p>
+See the Device Administration API sample for a complete example of how to enable storage encryption.
+</p>
\ No newline at end of file
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
index bcf6afe..40a5e18 100644
--- a/graphics/java/android/graphics/drawable/RippleBackground.java
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -42,8 +42,9 @@
     private static final float GLOBAL_SPEED = 1.0f;
     private static final float WAVE_TOUCH_DOWN_ACCELERATION = 1024.0f * GLOBAL_SPEED;
     private static final float WAVE_OPACITY_DECAY_VELOCITY = 3.0f / GLOBAL_SPEED;
-    private static final float WAVE_OUTER_OPACITY_VELOCITY_MAX = 4.5f * GLOBAL_SPEED;
-    private static final float WAVE_OUTER_OPACITY_VELOCITY_MIN = 1.5f * GLOBAL_SPEED;
+    private static final float WAVE_OUTER_OPACITY_EXIT_VELOCITY_MAX = 4.5f * GLOBAL_SPEED;
+    private static final float WAVE_OUTER_OPACITY_EXIT_VELOCITY_MIN = 1.5f * GLOBAL_SPEED;
+    private static final float WAVE_OUTER_OPACITY_ENTER_VELOCITY = 10.0f * GLOBAL_SPEED;
     private static final float WAVE_OUTER_SIZE_INFLUENCE_MAX = 200f;
     private static final float WAVE_OUTER_SIZE_INFLUENCE_MIN = 40f;
 
@@ -290,7 +291,7 @@
 
         final int radiusDuration = (int)
                 (1000 * Math.sqrt(mOuterRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensity) + 0.5);
-        final int outerDuration = (int) (1000 * 1.0f / WAVE_OUTER_OPACITY_VELOCITY_MIN);
+        final int outerDuration = (int) (1000 * 1.0f / WAVE_OUTER_OPACITY_ENTER_VELOCITY);
 
         final ObjectAnimator cX = ObjectAnimator.ofFloat(this, "xGravity", 1);
         cX.setAutoCancel(true);
@@ -333,8 +334,8 @@
         final float outerSizeInfluence = MathUtils.constrain(
                 (mOuterRadius - WAVE_OUTER_SIZE_INFLUENCE_MIN * mDensity)
                 / (WAVE_OUTER_SIZE_INFLUENCE_MAX * mDensity), 0, 1);
-        final float outerOpacityVelocity = MathUtils.lerp(WAVE_OUTER_OPACITY_VELOCITY_MIN,
-                WAVE_OUTER_OPACITY_VELOCITY_MAX, outerSizeInfluence);
+        final float outerOpacityVelocity = MathUtils.lerp(WAVE_OUTER_OPACITY_EXIT_VELOCITY_MIN,
+                WAVE_OUTER_OPACITY_EXIT_VELOCITY_MAX, outerSizeInfluence);
 
         // Determine at what time the inner and outer opacity intersect.
         // inner(t) = mOpacity - t * WAVE_OPACITY_DECAY_VELOCITY / 1000
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 11568d2..c65efe4 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1557,7 +1557,7 @@
     };
     const char16_t* valueToString(const Res_value* value, size_t stringBlock,
                                   char16_t tmpBuffer[TMP_BUFFER_SIZE],
-                                  size_t* outLen);
+                                  size_t* outLen) const;
 
     struct bag_entry {
         ssize_t stringBlock;
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 6ac49ee..0db8c77 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -331,6 +331,36 @@
         }
     }
 
+    public boolean resetUid(int uid) {
+        try {
+            mError = mBinder.reset_uid(uid);
+            return mError == NO_ERROR;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return false;
+        }
+    }
+
+    public boolean syncUid(int sourceUid, int targetUid) {
+        try {
+            mError = mBinder.sync_uid(sourceUid, targetUid);
+            return mError == NO_ERROR;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return false;
+        }
+    }
+
+    public boolean passwordUid(String password, int uid) {
+        try {
+            mError = mBinder.password_uid(password, uid);
+            return mError == NO_ERROR;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return false;
+        }
+    }
+
     public int getLastError() {
         return mError;
     }
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 3f014ef..690b1d6 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2939,7 +2939,7 @@
             for (size_t i = 0; i < bags->size(); i++) {
                 TABLE_NOISY(printf("type=%d\n", i));
                 const TypeList& typeList = types[i];
-                if (typeList.isEmpty()) {
+                if (!typeList.isEmpty()) {
                     bag_set** typeBags = bags->get(i);
                     TABLE_NOISY(printf("typeBags=%p\n", typeBags));
                     if (typeBags) {
@@ -3728,7 +3728,7 @@
 
 const char16_t* ResTable::valueToString(
     const Res_value* value, size_t stringBlock,
-    char16_t /*tmpBuffer*/ [TMP_BUFFER_SIZE], size_t* outLen)
+    char16_t /*tmpBuffer*/ [TMP_BUFFER_SIZE], size_t* outLen) const
 {
     if (!value) {
         return NULL;
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index a10c387..5808d20 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -26,6 +26,7 @@
     Idmap_test.cpp \
     ResTable_test.cpp \
     Split_test.cpp \
+    Theme_test.cpp \
     TypeWrappers_test.cpp \
     ZipUtils_test.cpp
 
diff --git a/libs/androidfw/tests/BackupData_test.cpp b/libs/androidfw/tests/BackupData_test.cpp
index 17f91ca..92af7fe 100644
--- a/libs/androidfw/tests/BackupData_test.cpp
+++ b/libs/androidfw/tests/BackupData_test.cpp
@@ -45,7 +45,7 @@
 class BackupDataTest : public testing::Test {
 protected:
     char* m_external_storage;
-    char* m_filename;
+    String8 mFilename;
     String8 mKey1;
     String8 mKey2;
     String8 mKey3;
@@ -53,15 +53,13 @@
 
     virtual void SetUp() {
         m_external_storage = getenv("EXTERNAL_STORAGE");
+        mFilename.append(m_external_storage);
+        mFilename.append(TEST_FILENAME);
 
-        const int totalLen = strlen(m_external_storage) + strlen(TEST_FILENAME) + 1;
-        m_filename = new char[totalLen];
-        snprintf(m_filename, totalLen, "%s%s", m_external_storage, TEST_FILENAME);
-
-        ::unlink(m_filename);
-        int fd = ::open(m_filename, O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+        ::unlink(mFilename.string());
+        int fd = ::open(mFilename.string(), O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
         if (fd < 0) {
-            FAIL() << "Couldn't create " << m_filename << " for writing";
+            FAIL() << "Couldn't create " << mFilename.string() << " for writing";
         }
         mKey1 = String8(KEY1);
         mKey2 = String8(KEY2);
@@ -74,7 +72,7 @@
 };
 
 TEST_F(BackupDataTest, WriteAndReadSingle) {
-  int fd = ::open(m_filename, O_WRONLY);
+  int fd = ::open(mFilename.string(), O_WRONLY);
   BackupDataWriter* writer = new BackupDataWriter(fd);
 
   EXPECT_EQ(NO_ERROR, writer->WriteEntityHeader(mKey1, sizeof(DATA1)))
@@ -83,7 +81,7 @@
           << "WriteEntityData returned an error";
 
   ::close(fd);
-  fd = ::open(m_filename, O_RDONLY);
+  fd = ::open(mFilename.string(), O_RDONLY);
   BackupDataReader* reader = new BackupDataReader(fd);
   EXPECT_EQ(NO_ERROR, reader->Status())
           << "Reader ctor failed";
@@ -116,7 +114,7 @@
 }
 
 TEST_F(BackupDataTest, WriteAndReadMultiple) {
-  int fd = ::open(m_filename, O_WRONLY);
+  int fd = ::open(mFilename.string(), O_WRONLY);
   BackupDataWriter* writer = new BackupDataWriter(fd);
   writer->WriteEntityHeader(mKey1, sizeof(DATA1));
   writer->WriteEntityData(DATA1, sizeof(DATA1));
@@ -124,7 +122,7 @@
   writer->WriteEntityData(DATA2, sizeof(DATA2));
 
   ::close(fd);
-  fd = ::open(m_filename, O_RDONLY);
+  fd = ::open(mFilename.string(), O_RDONLY);
   BackupDataReader* reader = new BackupDataReader(fd);
 
   bool done;
@@ -164,7 +162,7 @@
 }
 
 TEST_F(BackupDataTest, SkipEntity) {
-  int fd = ::open(m_filename, O_WRONLY);
+  int fd = ::open(mFilename.string(), O_WRONLY);
   BackupDataWriter* writer = new BackupDataWriter(fd);
   writer->WriteEntityHeader(mKey1, sizeof(DATA1));
   writer->WriteEntityData(DATA1, sizeof(DATA1));
@@ -174,7 +172,7 @@
   writer->WriteEntityData(DATA3, sizeof(DATA3));
 
   ::close(fd);
-  fd = ::open(m_filename, O_RDONLY);
+  fd = ::open(mFilename.string(), O_RDONLY);
   BackupDataReader* reader = new BackupDataReader(fd);
 
   bool done;
@@ -219,14 +217,14 @@
 }
 
 TEST_F(BackupDataTest, DeleteEntity) {
-  int fd = ::open(m_filename, O_WRONLY);
+  int fd = ::open(mFilename.string(), O_WRONLY);
   BackupDataWriter* writer = new BackupDataWriter(fd);
   writer->WriteEntityHeader(mKey1, sizeof(DATA1));
   writer->WriteEntityData(DATA1, sizeof(DATA1));
   writer->WriteEntityHeader(mKey2, -1);
 
   ::close(fd);
-  fd = ::open(m_filename, O_RDONLY);
+  fd = ::open(mFilename.string(), O_RDONLY);
   BackupDataReader* reader = new BackupDataReader(fd);
 
   bool done;
@@ -258,7 +256,7 @@
 }
 
 TEST_F(BackupDataTest, EneityAfterDelete) {
-  int fd = ::open(m_filename, O_WRONLY);
+  int fd = ::open(mFilename.string(), O_WRONLY);
   BackupDataWriter* writer = new BackupDataWriter(fd);
   writer->WriteEntityHeader(mKey1, sizeof(DATA1));
   writer->WriteEntityData(DATA1, sizeof(DATA1));
@@ -267,7 +265,7 @@
   writer->WriteEntityData(DATA3, sizeof(DATA3));
 
   ::close(fd);
-  fd = ::open(m_filename, O_RDONLY);
+  fd = ::open(mFilename.string(), O_RDONLY);
   BackupDataReader* reader = new BackupDataReader(fd);
 
   bool done;
@@ -319,7 +317,7 @@
 }
 
 TEST_F(BackupDataTest, OnlyDeleteEntities) {
-  int fd = ::open(m_filename, O_WRONLY);
+  int fd = ::open(mFilename.string(), O_WRONLY);
   BackupDataWriter* writer = new BackupDataWriter(fd);
   writer->WriteEntityHeader(mKey1, -1);
   writer->WriteEntityHeader(mKey2, -1);
@@ -327,7 +325,7 @@
   writer->WriteEntityHeader(mKey4, -1);
 
   ::close(fd);
-  fd = ::open(m_filename, O_RDONLY);
+  fd = ::open(mFilename.string(), O_RDONLY);
   BackupDataReader* reader = new BackupDataReader(fd);
 
   bool done;
@@ -387,13 +385,13 @@
 }
 
 TEST_F(BackupDataTest, ReadDeletedEntityData) {
-  int fd = ::open(m_filename, O_WRONLY);
+  int fd = ::open(mFilename.string(), O_WRONLY);
   BackupDataWriter* writer = new BackupDataWriter(fd);
   writer->WriteEntityHeader(mKey1, -1);
   writer->WriteEntityHeader(mKey2, -1);
 
   ::close(fd);
-  fd = ::open(m_filename, O_RDONLY);
+  fd = ::open(mFilename.string(), O_RDONLY);
   BackupDataReader* reader = new BackupDataReader(fd);
 
   bool done;
@@ -431,6 +429,7 @@
   EXPECT_EQ(-1, (int) dataSize)
           << "not recognizing deletion on second entity";
 
+  delete[] dataBytes;
   delete writer;
   delete reader;
 }
diff --git a/libs/androidfw/tests/ObbFile_test.cpp b/libs/androidfw/tests/ObbFile_test.cpp
index 7a4dd13..1151121 100644
--- a/libs/androidfw/tests/ObbFile_test.cpp
+++ b/libs/androidfw/tests/ObbFile_test.cpp
@@ -34,20 +34,18 @@
 class ObbFileTest : public testing::Test {
 protected:
     sp<ObbFile> mObbFile;
-    char* mExternalStorage;
-    char* mFileName;
+    String8 mFileName;
 
     virtual void SetUp() {
         mObbFile = new ObbFile();
-        mExternalStorage = getenv("EXTERNAL_STORAGE");
+        char* externalStorage = getenv("EXTERNAL_STORAGE");
 
-        const int totalLen = strlen(mExternalStorage) + strlen(TEST_FILENAME) + 1;
-        mFileName = new char[totalLen];
-        snprintf(mFileName, totalLen, "%s%s", mExternalStorage, TEST_FILENAME);
+        mFileName.append(externalStorage);
+        mFileName.append(TEST_FILENAME);
 
-        int fd = ::open(mFileName, O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+        int fd = ::open(mFileName.string(), O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
         if (fd < 0) {
-            FAIL() << "Couldn't create " << mFileName << " for tests";
+            FAIL() << "Couldn't create " << mFileName.string() << " for tests";
         }
     }
 
@@ -71,12 +69,12 @@
     EXPECT_TRUE(mObbFile->setSalt(salt, SALT_SIZE))
             << "Salt should be successfully set";
 
-    EXPECT_TRUE(mObbFile->writeTo(mFileName))
+    EXPECT_TRUE(mObbFile->writeTo(mFileName.string()))
             << "couldn't write to fake .obb file";
 
     mObbFile = new ObbFile();
 
-    EXPECT_TRUE(mObbFile->readFrom(mFileName))
+    EXPECT_TRUE(mObbFile->readFrom(mFileName.string()))
             << "couldn't read from fake .obb file";
 
     EXPECT_EQ(versionNum, mObbFile->getVersion())
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
new file mode 100644
index 0000000..4d07130
--- /dev/null
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#include <androidfw/ResourceTypes.h>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include "TestHelpers.h"
+#include "data/system/R.h"
+#include "data/app/R.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+namespace {
+
+#include "data/system/system_arsc.h"
+#include "data/app/app_arsc.h"
+
+enum { MAY_NOT_BE_BAG = false };
+
+/**
+ * TODO(adamlesinski): Enable when fixed.
+ */
+TEST(ThemeTest, DISABLED_shouldCopyThemeFromDifferentResTable) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(system_arsc, system_arsc_len));
+    ASSERT_EQ(NO_ERROR, table.add(app_arsc, app_arsc_len));
+
+    ResTable::Theme theme1(table);
+    ASSERT_EQ(NO_ERROR, theme1.applyStyle(app::R::style::Theme_One));
+    Res_value val;
+    ASSERT_GE(theme1.getAttribute(android::R::attr::background, &val), 0);
+    ASSERT_EQ(Res_value::TYPE_INT_COLOR_RGB8, val.dataType);
+    ASSERT_EQ(uint32_t(0xffff0000), val.data);
+    ASSERT_GE(theme1.getAttribute(app::R::attr::number, &val), 0);
+    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+    ASSERT_EQ(uint32_t(1), val.data);
+
+    ResTable table2;
+    ASSERT_EQ(NO_ERROR, table2.add(system_arsc, system_arsc_len));
+    ASSERT_EQ(NO_ERROR, table2.add(app_arsc, app_arsc_len));
+
+    ResTable::Theme theme2(table2);
+    ASSERT_EQ(NO_ERROR, theme2.setTo(theme1));
+    ASSERT_GE(theme2.getAttribute(android::R::attr::background, &val), 0);
+    ASSERT_EQ(Res_value::TYPE_INT_COLOR_RGB8, val.dataType);
+    ASSERT_EQ(uint32_t(0xffff0000), val.data);
+    ASSERT_GE(theme2.getAttribute(app::R::attr::number, &val), 0);
+    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+    ASSERT_EQ(uint32_t(1), val.data);
+}
+
+}
diff --git a/packages/DocumentsUI/res/drawable/grid_protect_background.xml b/libs/androidfw/tests/data/app/AndroidManifest.xml
similarity index 72%
rename from packages/DocumentsUI/res/drawable/grid_protect_background.xml
rename to libs/androidfw/tests/data/app/AndroidManifest.xml
index 2e7aadd..bfa3a39 100644
--- a/packages/DocumentsUI/res/drawable/grid_protect_background.xml
+++ b/libs/androidfw/tests/data/app/AndroidManifest.xml
@@ -14,11 +14,6 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false">
-        <color android:color="#88000000" />
-    </item>
-    <item>
-        <color android:color="#88252525" />
-    </item>
-</selector>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.app">
+</manifest>
diff --git a/libs/androidfw/tests/data/app/R.h b/libs/androidfw/tests/data/app/R.h
new file mode 100644
index 0000000..780a116
--- /dev/null
+++ b/libs/androidfw/tests/data/app/R.h
@@ -0,0 +1,22 @@
+#ifndef __APP_R_H
+#define __APP_R_H
+
+namespace app {
+namespace R {
+
+namespace attr {
+    enum {
+        number         = 0x7f010000,   // default
+    };
+}
+
+namespace style {
+    enum {
+        Theme_One      = 0x7f020000,   // default
+    };
+}
+
+} // namespace R
+} // namespace app
+
+#endif // __APP_R_H
diff --git a/libs/androidfw/tests/data/app/app_arsc.h b/libs/androidfw/tests/data/app/app_arsc.h
new file mode 100644
index 0000000..d5d9a3b
--- /dev/null
+++ b/libs/androidfw/tests/data/app/app_arsc.h
@@ -0,0 +1,62 @@
+unsigned char app_arsc[] = {
+  0x02, 0x00, 0x0c, 0x00, 0xc4, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x9c, 0x02, 0x00, 0x00,
+  0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
+  0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
+  0x64, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
+  0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00,
+  0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
+  0x4c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6e, 0x00,
+  0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00,
+  0x09, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00,
+  0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+  0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+  0x64, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00
+};
+unsigned int app_arsc_len = 708;
diff --git a/libs/androidfw/tests/data/app/build b/libs/androidfw/tests/data/app/build
new file mode 100755
index 0000000..89c4641
--- /dev/null
+++ b/libs/androidfw/tests/data/app/build
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+aapt package -v -I ../system/bundle.apk -M AndroidManifest.xml -S res -F bundle.apk -f && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc app.arsc && \
+xxd -i app.arsc > app_arsc.h
diff --git a/libs/androidfw/tests/data/app/res/values/values.xml b/libs/androidfw/tests/data/app/res/values/values.xml
new file mode 100644
index 0000000..b0ead38
--- /dev/null
+++ b/libs/androidfw/tests/data/app/res/values/values.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <attr name="number" format="integer"/>
+    <style name="Theme.One" parent="@android:style/Theme.One">
+        <item name="number">1</item>
+    </style>
+</resources>
diff --git a/packages/DocumentsUI/res/drawable/grid_protect_background.xml b/libs/androidfw/tests/data/system/AndroidManifest.xml
similarity index 72%
copy from packages/DocumentsUI/res/drawable/grid_protect_background.xml
copy to libs/androidfw/tests/data/system/AndroidManifest.xml
index 2e7aadd..af105ee 100644
--- a/packages/DocumentsUI/res/drawable/grid_protect_background.xml
+++ b/libs/androidfw/tests/data/system/AndroidManifest.xml
@@ -14,11 +14,6 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false">
-        <color android:color="#88000000" />
-    </item>
-    <item>
-        <color android:color="#88252525" />
-    </item>
-</selector>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android">
+</manifest>
diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h
new file mode 100644
index 0000000..7a9d3db
--- /dev/null
+++ b/libs/androidfw/tests/data/system/R.h
@@ -0,0 +1,23 @@
+#ifndef __ANDROID_R_H
+#define __ANDROID_R_H
+
+namespace android {
+namespace R {
+
+namespace attr {
+    enum {
+        background  = 0x01010000, // default
+        foreground  = 0x01010001, // default
+    };
+}
+
+namespace style {
+    enum {
+        Theme_One      = 0x01020000,   // default
+    };
+}
+
+} // namespace R
+} // namespace android
+
+#endif // __ANDROID_R_H
diff --git a/libs/androidfw/tests/data/system/build b/libs/androidfw/tests/data/system/build
new file mode 100755
index 0000000..2a3ac0b
--- /dev/null
+++ b/libs/androidfw/tests/data/system/build
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+aapt package -x -M AndroidManifest.xml -S res -F bundle.apk -f && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc system.arsc && \
+xxd -i system.arsc > system_arsc.h
diff --git a/libs/androidfw/tests/data/system/res/values/themes.xml b/libs/androidfw/tests/data/system/res/values/themes.xml
new file mode 100644
index 0000000..b29848e
--- /dev/null
+++ b/libs/androidfw/tests/data/system/res/values/themes.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <public name="background" type="attr" id="0x01010000"/>
+    <public name="foreground" type="attr" id="0x01010001"/>
+    <public name="Theme.One" type="style" id="0x01020000"/>
+
+    <attr name="background" format="color|reference"/>
+    <attr name="foreground" format="color|reference"/>
+    <style name="Theme.One" parent="">
+        <item name="android:background">#ff0000</item>
+        <item name="android:foreground">#000000</item>
+    </style>
+</resources>
diff --git a/libs/androidfw/tests/data/system/system_arsc.h b/libs/androidfw/tests/data/system/system_arsc.h
new file mode 100644
index 0000000..215ecae
--- /dev/null
+++ b/libs/androidfw/tests/data/system/system_arsc.h
@@ -0,0 +1,69 @@
+unsigned char system_arsc[] = {
+  0x02, 0x00, 0x0c, 0x00, 0x18, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xf0, 0x02, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
+  0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
+  0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00,
+  0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
+  0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x0a, 0x00, 0x62, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x67, 0x00,
+  0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00,
+  0x0a, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x67, 0x00,
+  0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00,
+  0x09, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00,
+  0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40,
+  0x01, 0x02, 0x44, 0x00, 0x84, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+  0x08, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x44, 0x00,
+  0x70, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0xff, 0xff,
+  0x01, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xff
+};
+unsigned int system_arsc_len = 792;
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp
index 9cc83ed..7834ef8 100644
--- a/libs/hwui/AmbientShadow.cpp
+++ b/libs/hwui/AmbientShadow.cpp
@@ -16,6 +16,43 @@
 
 #define LOG_TAG "OpenGLRenderer"
 
+/**
+ * Extra vertices for the corner for smoother corner.
+ * Only for outer vertices.
+ * Note that we use such extra memory to avoid an extra loop.
+ */
+// For half circle, we could add EXTRA_VERTEX_PER_PI vertices.
+// Set to 1 if we don't want to have any.
+#define EXTRA_CORNER_VERTEX_PER_PI 12
+
+// For the whole polygon, the sum of all the deltas b/t normals is 2 * M_PI,
+// therefore, the maximum number of extra vertices will be twice bigger.
+#define MAX_EXTRA_CORNER_VERTEX_NUMBER  (2 * EXTRA_CORNER_VERTEX_PER_PI)
+
+// For each RADIANS_DIVISOR, we would allocate one more vertex b/t the normals.
+#define CORNER_RADIANS_DIVISOR (M_PI / EXTRA_CORNER_VERTEX_PER_PI)
+
+/**
+ * Extra vertices for the Edge for interpolation artifacts.
+ * Same value for both inner and outer vertices.
+ */
+#define EXTRA_EDGE_VERTEX_PER_PI 50
+
+#define MAX_EXTRA_EDGE_VERTEX_NUMBER  (2 * EXTRA_EDGE_VERTEX_PER_PI)
+
+#define EDGE_RADIANS_DIVISOR  (M_PI / EXTRA_EDGE_VERTEX_PER_PI)
+
+/**
+ * Other constants:
+ */
+// For the edge of the penumbra, the opacity is 0.
+#define OUTER_OPACITY (0.0f)
+
+// Once the alpha difference is greater than this threshold, we will allocate extra
+// edge vertices.
+// If this is set to negative value, then all the edge will be tessellated.
+#define ALPHA_THRESHOLD (0.1f / 255.0f)
+
 #include <math.h>
 #include <utils/Log.h>
 #include <utils/Vector.h>
@@ -23,11 +60,97 @@
 #include "AmbientShadow.h"
 #include "ShadowTessellator.h"
 #include "Vertex.h"
+#include "utils/MathUtils.h"
 
 namespace android {
 namespace uirenderer {
 
 /**
+ *  Local utility functions.
+ */
+inline Vector2 getNormalFromVertices(const Vector3* vertices, int current, int next) {
+    // Convert from Vector3 to Vector2 first.
+    Vector2 currentVertex = { vertices[current].x, vertices[current].y };
+    Vector2 nextVertex = { vertices[next].x, vertices[next].y };
+
+    return ShadowTessellator::calculateNormal(currentVertex, nextVertex);
+}
+
+// The input z value will be converted to be non-negative inside.
+// The output must be ranged from 0 to 1.
+inline float getAlphaFromFactoredZ(float factoredZ) {
+    return 1.0 / (1 + MathUtils::max(factoredZ, 0.0f));
+}
+
+inline float getTransformedAlphaFromAlpha(float alpha) {
+    return acosf(1.0f - 2.0f * alpha);
+}
+
+// The output is ranged from 0 to M_PI.
+inline float getTransformedAlphaFromFactoredZ(float factoredZ) {
+    return getTransformedAlphaFromAlpha(getAlphaFromFactoredZ(factoredZ));
+}
+
+inline int getExtraVertexNumber(const Vector2& vector1, const Vector2& vector2,
+        float divisor) {
+    // The formula is :
+    // extraNumber = floor(acos(dot(n1, n2)) / (M_PI / EXTRA_VERTEX_PER_PI))
+    // The value ranges for each step are:
+    // dot( ) --- [-1, 1]
+    // acos( )     --- [0, M_PI]
+    // floor(...)  --- [0, EXTRA_VERTEX_PER_PI]
+    float dotProduct = vector1.dot(vector2);
+    // TODO: Use look up table for the dotProduct to extraVerticesNumber
+    // computation, if needed.
+    float angle = acosf(dotProduct);
+    return (int) floor(angle / divisor);
+}
+
+inline void checkOverflow(int used, int total, const char* bufferName) {
+    LOG_ALWAYS_FATAL_IF(used > total, "Error: %s overflow!!! used %d, total %d",
+            bufferName, used, total);
+}
+
+inline int getEdgeExtraAndUpdateSpike(Vector2* currentSpike,
+        const Vector3& secondVertex, const Vector3& centroid) {
+    Vector2 secondSpike  = {secondVertex.x - centroid.x, secondVertex.y - centroid.y};
+    secondSpike.normalize();
+
+    int result = getExtraVertexNumber(secondSpike, *currentSpike, EDGE_RADIANS_DIVISOR);
+    *currentSpike = secondSpike;
+    return result;
+}
+
+// Given the caster's vertex count, compute all the buffers size depending on
+// whether or not the caster is opaque.
+inline void computeBufferSize(int* totalVertexCount, int* totalIndexCount,
+        int* totalUmbraCount, int casterVertexCount, bool isCasterOpaque) {
+    // Compute the size of the vertex buffer.
+    int outerVertexCount = casterVertexCount * 2 + MAX_EXTRA_CORNER_VERTEX_NUMBER +
+        MAX_EXTRA_EDGE_VERTEX_NUMBER;
+    int innerVertexCount = casterVertexCount + MAX_EXTRA_EDGE_VERTEX_NUMBER;
+    *totalVertexCount = outerVertexCount + innerVertexCount;
+
+    // Compute the size of the index buffer.
+    *totalIndexCount = 2 * outerVertexCount + 2;
+
+    // Compute the size of the umber buffer.
+    // For translucent object, keep track of the umbra(inner) vertex in order to draw
+    // inside. We only need to store the index information.
+    *totalUmbraCount = 0;
+    if (!isCasterOpaque) {
+        // Add the centroid if occluder is translucent.
+        *totalVertexCount++;
+        *totalIndexCount += 2 * innerVertexCount + 1;
+        *totalUmbraCount = innerVertexCount;
+    }
+}
+
+inline bool needsExtraForEdge(float firstAlpha, float secondAlpha) {
+    return abs(firstAlpha - secondAlpha) > ALPHA_THRESHOLD;
+}
+
+/**
  * Calculate the shadows as a triangle strips while alpha value as the
  * shadow values.
  *
@@ -43,290 +166,198 @@
  *
  * @param shadowVertexBuffer Return an floating point array of (x, y, a)
  *               triangle strips mode.
+ *
+ * An simple illustration:
+ * For now let's mark the outer vertex as Pi, the inner as Vi, the centroid as C.
+ *
+ * First project the occluder to the Z=0 surface.
+ * Then we got all the inner vertices. And we compute the normal for each edge.
+ * According to the normal, we generate outer vertices. E.g: We generate P1 / P4
+ * as extra corner vertices to make the corner looks round and smoother.
+ *
+ * Due to the fact that the alpha is not linear interpolated along the inner
+ * edge, when the alpha is different, we may add extra vertices such as P2.1, P2.2,
+ * V0.1, V0.2 to avoid the visual artifacts.
+ *
+ *                                            (P3)
+ *          (P2)     (P2.1)     (P2.2)         |     ' (P4)
+ *   (P1)'   |        |           |            |   '
+ *         ' |        |           |            | '
+ * (P0)  ------------------------------------------------(P5)
+ *           | (V0)   (V0.1)    (V0.2)         |(V1)
+ *           |                                 |
+ *           |                                 |
+ *           |               (C)               |
+ *           |                                 |
+ *           |                                 |
+ *           |                                 |
+ *           |                                 |
+ *        (V3)-----------------------------------(V2)
  */
 void AmbientShadow::createAmbientShadow(bool isCasterOpaque,
-        const Vector3* vertices, int vertexCount, const Vector3& centroid3d,
+        const Vector3* casterVertices, int casterVertexCount, const Vector3& centroid3d,
         float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) {
-    const int rays = SHADOW_RAY_COUNT;
-    // Validate the inputs.
-    if (vertexCount < 3 || heightFactor <= 0 || rays <= 0
-        || geomFactor <= 0) {
-#if DEBUG_SHADOW
-        ALOGW("Invalid input for createAmbientShadow(), early return!");
-#endif
-        return;
-    }
+    shadowVertexBuffer.setMode(VertexBuffer::kIndices);
 
-    Vector<Vector2> dir; // TODO: use C++11 unique_ptr
-    dir.setCapacity(rays);
-    float rayDist[rays];
-    float rayHeight[rays];
-    calculateRayDirections(rays, vertices, vertexCount, centroid3d, dir.editArray());
+    // In order to computer the outer vertices in one loop, we need pre-compute
+    // the normal by the vertex (n - 1) to vertex 0, and the spike and alpha value
+    // for vertex 0.
+    Vector2 previousNormal = getNormalFromVertices(casterVertices,
+            casterVertexCount - 1 , 0);
+    Vector2 currentSpike = {casterVertices[0].x - centroid3d.x,
+        casterVertices[0].y - centroid3d.y};
+    currentSpike.normalize();
+    float currentAlpha = getAlphaFromFactoredZ(casterVertices[0].z * heightFactor);
 
-    // Calculate the length and height of the points along the edge.
-    //
-    // The math here is:
-    // Intersect each ray (starting from the centroid) with the polygon.
-    for (int i = 0; i < rays; i++) {
-        int edgeIndex;
-        float edgeFraction;
-        float rayDistance;
-        calculateIntersection(vertices, vertexCount, centroid3d, dir[i], edgeIndex,
-                edgeFraction, rayDistance);
-        rayDist[i] = rayDistance;
-        if (edgeIndex < 0 || edgeIndex >= vertexCount) {
-#if DEBUG_SHADOW
-            ALOGW("Invalid edgeIndex!");
-#endif
-            edgeIndex = 0;
-        }
-        float h1 = vertices[edgeIndex].z;
-        float h2 = vertices[((edgeIndex + 1) % vertexCount)].z;
-        rayHeight[i] = h1 + edgeFraction * (h2 - h1);
-    }
-
-    // The output buffer length basically is roughly rays * layers, but since we
-    // need triangle strips, so we need to duplicate vertices to accomplish that.
+    // Preparing all the output data.
+    int totalVertexCount, totalIndexCount, totalUmbraCount;
+    computeBufferSize(&totalVertexCount, &totalIndexCount, &totalUmbraCount,
+            casterVertexCount, isCasterOpaque);
     AlphaVertex* shadowVertices =
-            shadowVertexBuffer.alloc<AlphaVertex>(SHADOW_VERTEX_COUNT);
+            shadowVertexBuffer.alloc<AlphaVertex>(totalVertexCount);
+    int vertexBufferIndex = 0;
+    uint16_t* indexBuffer = shadowVertexBuffer.allocIndices<uint16_t>(totalIndexCount);
+    int indexBufferIndex = 0;
+    uint16_t umbraVertices[totalUmbraCount];
+    int umbraIndex = 0;
 
-    // Calculate the vertex of the shadows.
-    //
-    // The math here is:
-    // Along the edges of the polygon, for each intersection point P (generated above),
-    // calculate the normal N, which should be perpendicular to the edge of the
-    // polygon (represented by the neighbor intersection points) .
-    // Shadow's vertices will be generated as : P + N * scale.
-    const Vector2 centroid2d = {centroid3d.x, centroid3d.y};
-    for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
-        Vector2 normal = {1.0f, 0.0f};
-        calculateNormal(rays, rayIndex, dir.array(), rayDist, normal);
+    for (int i = 0; i < casterVertexCount; i++)  {
+        // Corner: first figure out the extra vertices we need for the corner.
+        const Vector3& innerVertex = casterVertices[i];
+        Vector2 currentNormal = getNormalFromVertices(casterVertices, i,
+                (i + 1) % casterVertexCount);
 
-        // The vertex should be start from rayDist[i] then scale the
-        // normalizeNormal!
-        Vector2 intersection = dir[rayIndex] * rayDist[rayIndex] +
-                centroid2d;
+        int extraVerticesNumber = getExtraVertexNumber(currentNormal, previousNormal,
+                CORNER_RADIANS_DIVISOR);
 
-        // outer ring of points, expanded based upon height of each ray intersection
-        float expansionDist = rayHeight[rayIndex] * heightFactor *
-                geomFactor;
-        AlphaVertex::set(&shadowVertices[rayIndex],
-                intersection.x + normal.x * expansionDist,
-                intersection.y + normal.y * expansionDist,
-                0.0f);
-
-        // inner ring of points
-        float opacity = 1.0 / (1 + rayHeight[rayIndex] * heightFactor);
-        // NOTE: Shadow alpha values are transformed when stored in alphavertices,
-        // so that they can be consumed directly by gFS_Main_ApplyVertexAlphaShadowInterp
-        float transformedOpacity = acos(1.0f - 2.0f * opacity);
-        AlphaVertex::set(&shadowVertices[rays + rayIndex],
-                intersection.x,
-                intersection.y,
-                transformedOpacity);
-    }
-
-    if (isCasterOpaque) {
-        // skip inner ring, calc bounds over filled portion of buffer
-        shadowVertexBuffer.computeBounds<AlphaVertex>(2 * rays);
-        shadowVertexBuffer.setMode(VertexBuffer::kOnePolyRingShadow);
-    } else {
-        // If caster isn't opaque, we need to to fill the umbra by storing the umbra's
-        // centroid in the innermost ring of vertices.
-        float centroidAlpha = 1.0 / (1 + centroid3d.z * heightFactor);
-        AlphaVertex centroidXYA;
-        AlphaVertex::set(&centroidXYA, centroid2d.x, centroid2d.y, centroidAlpha);
-        for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
-            shadowVertices[2 * rays + rayIndex] = centroidXYA;
-        }
-        // calc bounds over entire buffer
-        shadowVertexBuffer.computeBounds<AlphaVertex>();
-        shadowVertexBuffer.setMode(VertexBuffer::kTwoPolyRingShadow);
-    }
-
+        float expansionDist = innerVertex.z * heightFactor * geomFactor;
+        const int cornerSlicesNumber = extraVerticesNumber + 1; // Minimal as 1.
 #if DEBUG_SHADOW
-    for (int i = 0; i < SHADOW_VERTEX_COUNT; i++) {
-        ALOGD("ambient shadow value: i %d, (x:%f, y:%f, a:%f)", i, shadowVertices[i].x,
-                shadowVertices[i].y, shadowVertices[i].alpha);
-    }
+        ALOGD("cornerSlicesNumber is %d", cornerSlicesNumber);
 #endif
-}
 
-/**
- * Generate an array of rays' direction vectors.
- * To make sure the vertices generated are clockwise, the directions are from PI
- * to -PI.
- *
- * @param rays The number of rays shooting out from the centroid.
- * @param vertices Vertices of the polygon.
- * @param vertexCount The number of vertices.
- * @param centroid3d The centroid of the polygon.
- * @param dir Return the array of ray vectors.
- */
-void AmbientShadow::calculateRayDirections(const int rays, const Vector3* vertices,
-        const int vertexCount, const Vector3& centroid3d, Vector2* dir) {
-    // If we don't have enough rays, then fall back to the uniform distribution.
-    if (vertexCount * 2 > rays) {
-        float deltaAngle = 2 * M_PI / rays;
-        for (int i = 0; i < rays; i++) {
-            dir[i].x = cosf(M_PI - deltaAngle * i);
-            dir[i].y = sinf(M_PI - deltaAngle * i);
+        // Corner: fill the corner Vertex Buffer(VB) and Index Buffer(IB).
+        // We fill the inner vertex first, such that we can fill the index buffer
+        // inside the loop.
+        int currentInnerVertexIndex = vertexBufferIndex;
+        if (!isCasterOpaque) {
+            umbraVertices[umbraIndex++] = vertexBufferIndex;
         }
-        return;
-    }
+        AlphaVertex::set(&shadowVertices[vertexBufferIndex++], casterVertices[i].x,
+                casterVertices[i].y,
+                getTransformedAlphaFromAlpha(currentAlpha));
 
-    // If we have enough rays, then we assign each vertices a ray, and distribute
-    // the rest uniformly.
-    float rayThetas[rays];
+        const Vector3& innerStart = casterVertices[i];
 
-    const int uniformRayCount = rays - vertexCount;
-    const float deltaAngle = 2 * M_PI / uniformRayCount;
+        // outerStart is the first outer vertex for this inner vertex.
+        // outerLast is the last outer vertex for this inner vertex.
+        Vector2 outerStart = {0, 0};
+        Vector2 outerLast = {0, 0};
+        // This will create vertices from [0, cornerSlicesNumber] inclusively,
+        // which means minimally 2 vertices even without the extra ones.
+        for (int j = 0; j <= cornerSlicesNumber; j++) {
+            Vector2 averageNormal =
+                previousNormal * (cornerSlicesNumber - j) + currentNormal * j;
+            averageNormal /= cornerSlicesNumber;
+            averageNormal.normalize();
+            Vector2 outerVertex;
+            outerVertex.x = innerVertex.x + averageNormal.x * expansionDist;
+            outerVertex.y = innerVertex.y + averageNormal.y * expansionDist;
 
-    // We have to generate all the vertices' theta anyway and we also need to
-    // find the minimal, so let's precompute it first.
-    // Since the incoming polygon is clockwise, we can find the dip to identify
-    // the minimal theta.
-    float polyThetas[vertexCount];
-    int maxPolyThetaIndex = 0;
-    for (int i = 0; i < vertexCount; i++) {
-        polyThetas[i] = atan2(vertices[i].y - centroid3d.y,
-                vertices[i].x - centroid3d.x);
-        if (i > 0 && polyThetas[i] > polyThetas[i - 1]) {
-            maxPolyThetaIndex = i;
-        }
-    }
+            indexBuffer[indexBufferIndex++] = vertexBufferIndex;
+            indexBuffer[indexBufferIndex++] = currentInnerVertexIndex;
+            AlphaVertex::set(&shadowVertices[vertexBufferIndex++], outerVertex.x,
+                    outerVertex.y, OUTER_OPACITY);
 
-    // Both poly's thetas and uniform thetas are in decrease order(clockwise)
-    // from PI to -PI.
-    int polyThetaIndex = maxPolyThetaIndex;
-    float polyTheta = polyThetas[maxPolyThetaIndex];
-    int uniformThetaIndex = 0;
-    float uniformTheta = M_PI;
-    for (int i = 0; i < rays; i++) {
-        // Compare both thetas and pick the smaller one and move on.
-        bool hasThetaCollision = abs(polyTheta - uniformTheta) < MINIMAL_DELTA_THETA;
-        if (polyTheta > uniformTheta || hasThetaCollision) {
-            if (hasThetaCollision) {
-                // Shift the uniformTheta to middle way between current polyTheta
-                // and next uniform theta. The next uniform theta can wrap around
-                // to exactly PI safely here.
-                // Note that neither polyTheta nor uniformTheta can be FLT_MAX
-                // due to the hasThetaCollision is true.
-                uniformTheta = (polyTheta +  M_PI - deltaAngle * (uniformThetaIndex + 1)) / 2;
-#if DEBUG_SHADOW
-                ALOGD("Shifted uniformTheta to %f", uniformTheta);
-#endif
-            }
-            rayThetas[i] = polyTheta;
-            polyThetaIndex = (polyThetaIndex + 1) % vertexCount;
-            if (polyThetaIndex != maxPolyThetaIndex) {
-                polyTheta = polyThetas[polyThetaIndex];
-            } else {
-                // out of poly points.
-                polyTheta = - FLT_MAX;
-            }
-        } else {
-            rayThetas[i] = uniformTheta;
-            uniformThetaIndex++;
-            if (uniformThetaIndex < uniformRayCount) {
-                uniformTheta = M_PI - deltaAngle * uniformThetaIndex;
-            } else {
-                // out of uniform points.
-                uniformTheta = - FLT_MAX;
+            if (j == 0) {
+                outerStart = outerVertex;
+            } else if (j == cornerSlicesNumber) {
+                outerLast = outerVertex;
             }
         }
-    }
+        previousNormal = currentNormal;
 
-    for (int i = 0; i < rays; i++) {
+        // Edge: first figure out the extra vertices needed for the edge.
+        const Vector3& innerNext = casterVertices[(i + 1) % casterVertexCount];
+        float nextAlpha = getAlphaFromFactoredZ(innerNext.z * heightFactor);
+        if (needsExtraForEdge(currentAlpha, nextAlpha)) {
+            // TODO: See if we can / should cache this outer vertex across the loop.
+            Vector2 outerNext;
+            float expansionDist = innerNext.z * heightFactor * geomFactor;
+            outerNext.x = innerNext.x + currentNormal.x * expansionDist;
+            outerNext.y = innerNext.y + currentNormal.y * expansionDist;
+
+            // Compute the angle and see how many extra points we need.
+            int extraVerticesNumber = getEdgeExtraAndUpdateSpike(&currentSpike,
+                    innerNext, centroid3d);
 #if DEBUG_SHADOW
-        ALOGD("No. %d : %f", i, rayThetas[i] * 180 / M_PI);
+            ALOGD("extraVerticesNumber %d for edge %d", extraVerticesNumber, i);
 #endif
-        // TODO: Fix the intersection precision problem and remvoe the delta added
-        // here.
-        dir[i].x = cosf(rayThetas[i] + MINIMAL_DELTA_THETA);
-        dir[i].y = sinf(rayThetas[i] + MINIMAL_DELTA_THETA);
-    }
-}
+            // Edge: fill the edge's VB and IB.
+            // This will create vertices pair from [1, extraVerticesNumber - 1].
+            // If there is no extra vertices created here, the edge will be drawn
+            // as just 2 triangles.
+            for (int k = 1; k < extraVerticesNumber; k++) {
+                int startWeight = extraVerticesNumber - k;
+                Vector2 currentOuter =
+                    (outerLast * startWeight + outerNext * k) / extraVerticesNumber;
+                indexBuffer[indexBufferIndex++] = vertexBufferIndex;
+                AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentOuter.x,
+                        currentOuter.y, OUTER_OPACITY);
 
-/**
- * Calculate the intersection of a ray hitting the polygon.
- *
- * @param vertices The shadow caster's polygon, which is represented in a
- *                 Vector3 array.
- * @param vertexCount The length of caster's polygon in terms of number of vertices.
- * @param start The starting point of the ray.
- * @param dir The direction vector of the ray.
- *
- * @param outEdgeIndex Return the index of the segment (or index of the starting
- *                     vertex) that ray intersect with.
- * @param outEdgeFraction Return the fraction offset from the segment starting
- *                        index.
- * @param outRayDist Return the ray distance from centroid to the intersection.
- */
-void AmbientShadow::calculateIntersection(const Vector3* vertices, int vertexCount,
-        const Vector3& start, const Vector2& dir, int& outEdgeIndex,
-        float& outEdgeFraction, float& outRayDist) {
-    float startX = start.x;
-    float startY = start.y;
-    float dirX = dir.x;
-    float dirY = dir.y;
-    // Start the search from the last edge from poly[len-1] to poly[0].
-    int p1 = vertexCount - 1;
-
-    for (int p2 = 0; p2 < vertexCount; p2++) {
-        float p1x = vertices[p1].x;
-        float p1y = vertices[p1].y;
-        float p2x = vertices[p2].x;
-        float p2y = vertices[p2].y;
-
-        // The math here is derived from:
-        // f(t, v) = p1x * (1 - t) + p2x * t - (startX + dirX * v) = 0;
-        // g(t, v) = p1y * (1 - t) + p2y * t - (startY + dirY * v) = 0;
-        float div = (dirX * (p1y - p2y) + dirY * p2x - dirY * p1x);
-        if (div != 0) {
-            float t = (dirX * (p1y - startY) + dirY * startX - dirY * p1x) / (div);
-            if (t > 0 && t <= 1) {
-                float t2 = (p1x * (startY - p2y)
-                            + p2x * (p1y - startY)
-                            + startX * (p2y - p1y)) / div;
-                if (t2 > 0) {
-                    outEdgeIndex = p1;
-                    outRayDist = t2;
-                    outEdgeFraction = t;
-                    return;
+                if (!isCasterOpaque) {
+                    umbraVertices[umbraIndex++] = vertexBufferIndex;
                 }
+                Vector3 currentInner =
+                    (innerStart * startWeight + innerNext * k) / extraVerticesNumber;
+                indexBuffer[indexBufferIndex++] = vertexBufferIndex;
+                AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentInner.x,
+                        currentInner.y,
+                        getTransformedAlphaFromFactoredZ(currentInner.z * heightFactor));
             }
         }
-        p1 = p2;
+        currentAlpha = nextAlpha;
     }
-    return;
-};
 
-/**
- * Calculate the normal at the intersection point between a ray and the polygon.
- *
- * @param rays The total number of rays.
- * @param currentRayIndex The index of the ray which the normal is based on.
- * @param dir The array of the all the rays directions.
- * @param rayDist The pre-computed ray distances array.
- *
- * @param normal Return the normal.
- */
-void AmbientShadow::calculateNormal(int rays, int currentRayIndex,
-        const Vector2* dir, const float* rayDist, Vector2& normal) {
-    int preIndex = (currentRayIndex - 1 + rays) % rays;
-    int postIndex = (currentRayIndex + 1) % rays;
-    Vector2 p1 = dir[preIndex] * rayDist[preIndex];
-    Vector2 p2 = dir[postIndex] * rayDist[postIndex];
+    indexBuffer[indexBufferIndex++] = 1;
+    indexBuffer[indexBufferIndex++] = 0;
 
-    // Now the rays are going CW around the poly.
-    Vector2 delta = p2 - p1;
-    if (delta.length() != 0) {
-        delta.normalize();
-        // Calculate the normal , which is CCW 90 rotate to the delta.
-        normal.x = - delta.y;
-        normal.y = delta.x;
+    if (!isCasterOpaque) {
+        // Add the centroid as the last one in the vertex buffer.
+        float centroidOpacity =
+            getTransformedAlphaFromFactoredZ(centroid3d.z * heightFactor);
+        int centroidIndex = vertexBufferIndex;
+        AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid3d.x,
+                centroid3d.y, centroidOpacity);
+
+        for (int i = 0; i < umbraIndex; i++) {
+            // Note that umbraVertices[0] is always 0.
+            // So the start and the end of the umbra are using the "0".
+            // And penumbra ended with 0, so a degenerated triangle is formed b/t
+            // the umbra and penumbra.
+            indexBuffer[indexBufferIndex++] = umbraVertices[i];
+            indexBuffer[indexBufferIndex++] = centroidIndex;
+        }
+        indexBuffer[indexBufferIndex++] = 0;
     }
+
+    // At the end, update the real index and vertex buffer size.
+    shadowVertexBuffer.updateVertexCount(vertexBufferIndex);
+    shadowVertexBuffer.updateIndexCount(indexBufferIndex);
+
+    checkOverflow(vertexBufferIndex, totalVertexCount, "Vertex Buffer");
+    checkOverflow(indexBufferIndex, totalIndexCount, "Index Buffer");
+    checkOverflow(umbraIndex, totalUmbraCount, "Umbra Buffer");
+
+#if DEBUG_SHADOW
+    for (int i = 0; i < vertexBufferIndex; i++) {
+        ALOGD("vertexBuffer i %d, (%f, %f %f)", i, shadowVertices[i].x, shadowVertices[i].y,
+                shadowVertices[i].alpha);
+    }
+    for (int i = 0; i < indexBufferIndex; i++) {
+        ALOGD("indexBuffer i %d, indexBuffer[i] %d", i, indexBuffer[i]);
+    }
+#endif
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/AmbientShadow.h b/libs/hwui/AmbientShadow.h
index 68df246..9660dc0 100644
--- a/libs/hwui/AmbientShadow.h
+++ b/libs/hwui/AmbientShadow.h
@@ -28,27 +28,12 @@
 
 /**
  * AmbientShadow is used to calculate the ambient shadow value around a polygon.
- *
- * TODO: calculateIntersection() now is O(N*M), where N is the number of
- * polygon's vertics and M is the number of rays. In fact, by staring tracing
- * the vertex from the previous intersection, the algorithm can be O(N + M);
  */
 class AmbientShadow {
 public:
     static void createAmbientShadow(bool isCasterOpaque, const Vector3* poly,
             int polyLength, const Vector3& centroid3d, float heightFactor,
             float geomFactor, VertexBuffer& shadowVertexBuffer);
-
-private:
-    static void calculateRayDirections(const int rays, const Vector3* vertices,
-            const int vertexCount, const Vector3& centroid3d, Vector2* dir);
-
-    static void calculateIntersection(const Vector3* poly, int nbVertices,
-            const Vector3& start, const Vector2& dir, int& outEdgeIndex,
-            float& outEdgeFraction, float& outRayDist);
-
-    static void calculateNormal(int rays, int currentRayIndex, const Vector2* dir,
-            const float* rayDist, Vector2& normal);
 }; // AmbientShadow
 
 }; // namespace uirenderer
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 1c697d5..da65f38 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -95,6 +95,8 @@
         // Oh boy, we're starting! Man the battle stations!
         if (mPlayState == RUNNING) {
             transitionToRunning(context);
+        } else if (mPlayState == FINISHED) {
+            callOnFinishedListener(context);
         }
     }
 }
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index bbf0551..0f36c06 100755
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2417,6 +2417,10 @@
     } else if (mode == VertexBuffer::kTwoPolyRingShadow) {
         mCaches.bindShadowIndicesBuffer();
         glDrawElements(GL_TRIANGLE_STRIP, TWO_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0);
+    } else if (mode == VertexBuffer::kIndices) {
+        mCaches.unbindIndicesBuffer();
+        glDrawElements(GL_TRIANGLE_STRIP, vertexBuffer.getIndexCount(), GL_UNSIGNED_SHORT,
+                vertexBuffer.getIndices());
     }
 
     if (isAA) {
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
index 2a9f01c..d033ed9 100644
--- a/libs/hwui/Vector.h
+++ b/libs/hwui/Vector.h
@@ -111,6 +111,23 @@
     float y;
     float z;
 
+    Vector3 operator+(const Vector3& v) const {
+        return (Vector3){x + v.x, y + v.y, z + v.z};
+    }
+
+    Vector3 operator-(const Vector3& v) const {
+        return (Vector3){x - v.x, y - v.y, z - v.z};
+    }
+
+    Vector3 operator/(float s) const {
+        return (Vector3){x / s, y / s, z / s};
+    }
+
+    Vector3 operator*(float s) const {
+        return (Vector3){x * s, y * s, z * s};
+    }
+
+
     void dump() {
         ALOGD("Vector3[%.2f, %.2f, %.2f]", x, y, z);
     }
diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h
index 3837f88..966fa4e 100644
--- a/libs/hwui/VertexBuffer.h
+++ b/libs/hwui/VertexBuffer.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_HWUI_VERTEX_BUFFER_H
 #define ANDROID_HWUI_VERTEX_BUFFER_H
 
+#include "utils/MathUtils.h"
 
 namespace android {
 namespace uirenderer {
@@ -26,19 +27,27 @@
     enum Mode {
         kStandard = 0,
         kOnePolyRingShadow = 1,
-        kTwoPolyRingShadow = 2
+        kTwoPolyRingShadow = 2,
+        kIndices = 3
     };
 
     VertexBuffer()
             : mBuffer(0)
+            , mIndices(0)
             , mVertexCount(0)
+            , mIndexCount(0)
+            , mAllocatedVertexCount(0)
+            , mAllocatedIndexCount(0)
             , mByteCount(0)
             , mMode(kStandard)
+            , mReallocBuffer(0)
             , mCleanupMethod(NULL)
+            , mCleanupIndexMethod(NULL)
     {}
 
     ~VertexBuffer() {
         if (mCleanupMethod) mCleanupMethod(mBuffer);
+        if (mCleanupIndexMethod) mCleanupIndexMethod(mIndices);
     }
 
     /**
@@ -59,6 +68,7 @@
             mReallocBuffer = reallocBuffer + vertexCount;
             return reallocBuffer;
         }
+        mAllocatedVertexCount = vertexCount;
         mVertexCount = vertexCount;
         mByteCount = mVertexCount * sizeof(TYPE);
         mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount];
@@ -69,6 +79,17 @@
     }
 
     template <class TYPE>
+    TYPE* allocIndices(int indexCount) {
+        mAllocatedIndexCount = indexCount;
+        mIndexCount = indexCount;
+        mIndices = (void*)new TYPE[indexCount];
+
+        mCleanupIndexMethod = &(cleanup<TYPE>);
+
+        return (TYPE*)mIndices;
+    }
+
+    template <class TYPE>
     void copyInto(const VertexBuffer& srcBuffer, float xOffset, float yOffset) {
         int verticesToCopy = srcBuffer.getVertexCount();
 
@@ -103,9 +124,17 @@
     }
 
     const void* getBuffer() const { return mBuffer; }
+    const void* getIndices() const { return mIndices; }
     const Rect& getBounds() const { return mBounds; }
     unsigned int getVertexCount() const { return mVertexCount; }
     unsigned int getSize() const { return mByteCount; }
+    unsigned int getIndexCount() const { return mIndexCount; }
+    void updateIndexCount(unsigned int newCount)  {
+        mIndexCount = MathUtils::min(newCount, mAllocatedIndexCount);
+    }
+    void updateVertexCount(unsigned int newCount)  {
+        newCount = MathUtils::min(newCount, mAllocatedVertexCount);
+    }
     Mode getMode() const { return mMode; }
 
     void setBounds(Rect bounds) { mBounds = bounds; }
@@ -127,14 +156,22 @@
     }
 
     Rect mBounds;
+
     void* mBuffer;
+    void* mIndices;
+
     unsigned int mVertexCount;
+    unsigned int mIndexCount;
+    unsigned int mAllocatedVertexCount;
+    unsigned int mAllocatedIndexCount;
     unsigned int mByteCount;
+
     Mode mMode;
 
     void* mReallocBuffer; // used for multi-allocation
 
     void (*mCleanupMethod)(void*);
+    void (*mCleanupIndexMethod)(void*);
 };
 
 }; // namespace uirenderer
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index ecfedf6..491a295 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -314,6 +314,7 @@
     stopDrawing();
     if (mEglManager.hasEglContext()) {
         requireGlContext();
+        freePrefetechedLayers();
         mRootRenderNode->destroyHardwareResources();
         Caches::getInstance().flush(Caches::kFlushMode_Layers);
     }
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 711831b..ae8ce4b 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -683,6 +683,7 @@
          *
          * @param mediaButtonIntent an intent containing the KeyEvent as an
          *            extra
+         * @return True if the event was handled, false otherwise.
          */
         public boolean onMediaButtonEvent(@NonNull Intent mediaButtonIntent) {
             if (mSession != null
@@ -695,36 +696,43 @@
                         case KeyEvent.KEYCODE_MEDIA_PLAY:
                             if ((validActions & PlaybackState.ACTION_PLAY) != 0) {
                                 onPlay();
+                                return true;
                             }
                             break;
                         case KeyEvent.KEYCODE_MEDIA_PAUSE:
                             if ((validActions & PlaybackState.ACTION_PAUSE) != 0) {
                                 onPause();
+                                return true;
                             }
                             break;
                         case KeyEvent.KEYCODE_MEDIA_NEXT:
                             if ((validActions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) {
                                 onSkipToNext();
+                                return true;
                             }
                             break;
                         case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
                             if ((validActions & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0) {
                                 onSkipToPrevious();
+                                return true;
                             }
                             break;
                         case KeyEvent.KEYCODE_MEDIA_STOP:
                             if ((validActions & PlaybackState.ACTION_STOP) != 0) {
                                 onStop();
+                                return true;
                             }
                             break;
                         case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
                             if ((validActions & PlaybackState.ACTION_FAST_FORWARD) != 0) {
                                 onFastForward();
+                                return true;
                             }
                             break;
                         case KeyEvent.KEYCODE_MEDIA_REWIND:
                             if ((validActions & PlaybackState.ACTION_REWIND) != 0) {
                                 onRewind();
+                                return true;
                             }
                             break;
                         case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
@@ -737,8 +745,10 @@
                                     | PlaybackState.ACTION_PAUSE)) != 0;
                             if (isPlaying && canPause) {
                                 onPause();
+                                return true;
                             } else if (!isPlaying && canPlay) {
                                 onPlay();
+                                return true;
                             }
                             break;
                     }
diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java
index 0bda2b3..ccc6ad8 100644
--- a/media/java/android/media/tv/TvContentRating.java
+++ b/media/java/android/media/tv/TvContentRating.java
@@ -136,7 +136,7 @@
  * <table>
  *     <tr>
  *         <th>Constant Value</th>
- *         <th>Comment</th>
+ *         <th>Description</th>
  *     </tr>
  *     <tr>
  *         <td>com.android.tv</td>
@@ -148,7 +148,7 @@
  * <table>
  *     <tr>
  *         <th>Constant Value</th>
- *         <th>Comment</th>
+ *         <th>Description</th>
  *     </tr>
  *     <tr>
  *         <td>AM_TV_RS</td>
@@ -346,7 +346,7 @@
  *     <tr>
  *         <th>Rating System</th>
  *         <th>Constant Value</th>
- *         <th>Comment</th>
+ *         <th>Description</th>
  *     </tr>
  *     <tr>
  *         <td valign="top" rowspan="6">AM_TV_RS</td>
@@ -1419,7 +1419,7 @@
  *     <tr>
  *         <th>Rating System</th>
  *         <th>Constant Value</th>
- *         <th>Comment</th>
+ *         <th>Description</th>
  *     </tr>
  *     <tr>
  *         <td valign="top" rowspan="6">NL_TV</td>
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 00c7ad4..7f1c304 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -680,7 +680,7 @@
          * <p>
          * A value of 1 indicates the channel is included in the channel list that applications use
          * to browse channels, a value of 0 indicates the channel is not included in the list. If
-         * not specified, this value is set to 1 (browsable) by default.
+         * not specified, this value is set to 0 (not browsable) by default.
          * </p><p>
          * Type: INTEGER (boolean)
          * </p>
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index c678ac7..0fed27e 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -263,7 +263,7 @@
 
     String8 vendorMessage;
     if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
-        vendorMessage.format("DRM vendor-defined error: %d", err);
+        vendorMessage = String8::format("DRM vendor-defined error: %d", err);
         drmMessage = vendorMessage.string();
     }
 
@@ -285,7 +285,7 @@
             if (msg == NULL) {
                 msg = drmMessage;
             } else {
-                errbuf.format("%s: %s", msg, drmMessage);
+                errbuf = String8::format("%s: %s", msg, drmMessage);
                 msg = errbuf.string();
             }
         }
diff --git a/packages/DocumentsUI/res/drawable/item_activated_overlay.xml b/packages/DocumentsUI/res/color/item_root_icon.xml
similarity index 73%
copy from packages/DocumentsUI/res/drawable/item_activated_overlay.xml
copy to packages/DocumentsUI/res/color/item_root_icon.xml
index 83e4d7e..1374e61 100644
--- a/packages/DocumentsUI/res/drawable/item_activated_overlay.xml
+++ b/packages/DocumentsUI/res/color/item_root_icon.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
+<!-- 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.
@@ -15,11 +15,7 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true" android:state_activated="true">
-        <color android:color="@color/accent_color_overlay" />
-    </item>
-    <item android:state_focused="false" android:state_activated="true">
-        <color android:color="@color/accent_color_overlay" />
-    </item>
-    <item android:drawable="@android:color/transparent" />
+    <item android:state_focused="true" android:state_activated="true" android:color="@*android:color/primary_text_default_material_dark" />
+    <item android:state_focused="false" android:state_activated="true" android:color="@*android:color/primary_text_default_material_dark" />
+    <item android:color="@*android:color/secondary_text_material_light" />
 </selector>
diff --git a/packages/DocumentsUI/res/drawable/item_activated_overlay.xml b/packages/DocumentsUI/res/color/item_root_primary_text.xml
similarity index 63%
copy from packages/DocumentsUI/res/drawable/item_activated_overlay.xml
copy to packages/DocumentsUI/res/color/item_root_primary_text.xml
index 83e4d7e..7e703fa 100644
--- a/packages/DocumentsUI/res/drawable/item_activated_overlay.xml
+++ b/packages/DocumentsUI/res/color/item_root_primary_text.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
+<!-- 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.
@@ -15,11 +15,8 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true" android:state_activated="true">
-        <color android:color="@color/accent_color_overlay" />
-    </item>
-    <item android:state_focused="false" android:state_activated="true">
-        <color android:color="@color/accent_color_overlay" />
-    </item>
-    <item android:drawable="@android:color/transparent" />
+    <item android:state_focused="true" android:state_activated="true" android:color="@*android:color/primary_text_default_material_dark" />
+    <item android:state_focused="false" android:state_activated="true" android:color="@*android:color/primary_text_default_material_dark" />
+    <item android:state_enabled="false" android:alpha="@*android:dimen/disabled_alpha_material_light" android:color="@*android:color/primary_text_default_material_light" />
+    <item android:color="@*android:color/primary_text_default_material_light" />
 </selector>
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_grid_selection_check.png b/packages/DocumentsUI/res/drawable-hdpi/ic_grid_selection_check.png
new file mode 100644
index 0000000..f3007c2
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_grid_selection_check.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_grid_selection_check.png b/packages/DocumentsUI/res/drawable-mdpi/ic_grid_selection_check.png
new file mode 100644
index 0000000..16f2ab9
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_grid_selection_check.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_grid_selection_check.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_grid_selection_check.png
new file mode 100644
index 0000000..0885320
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_grid_selection_check.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_grid_selection_check.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_grid_selection_check.png
new file mode 100644
index 0000000..083bbcc
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_grid_selection_check.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_grid_selection_check.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_grid_selection_check.png
new file mode 100644
index 0000000..74b1ca5
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_grid_selection_check.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable/item_activated_overlay.xml b/packages/DocumentsUI/res/drawable/item_doc_grid_overlay.xml
similarity index 79%
copy from packages/DocumentsUI/res/drawable/item_activated_overlay.xml
copy to packages/DocumentsUI/res/drawable/item_doc_grid_overlay.xml
index 83e4d7e..3fbd25e 100644
--- a/packages/DocumentsUI/res/drawable/item_activated_overlay.xml
+++ b/packages/DocumentsUI/res/drawable/item_doc_grid_overlay.xml
@@ -15,11 +15,8 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true" android:state_activated="true">
-        <color android:color="@color/accent_color_overlay" />
-    </item>
-    <item android:state_focused="false" android:state_activated="true">
-        <color android:color="@color/accent_color_overlay" />
-    </item>
+    <item android:state_focused="true" android:state_activated="true" android:drawable="@color/item_doc_grid_overlay_activated" />
+    <item android:state_focused="false" android:state_activated="true" android:drawable="@color/item_doc_grid_overlay_activated" />
+    <item android:state_enabled="false" android:drawable="@color/item_doc_grid_overlay_disabled" />
     <item android:drawable="@android:color/transparent" />
 </selector>
diff --git a/packages/DocumentsUI/res/drawable/item_activated_overlay.xml b/packages/DocumentsUI/res/drawable/item_doc_grid_overlay_icon.xml
similarity index 79%
copy from packages/DocumentsUI/res/drawable/item_activated_overlay.xml
copy to packages/DocumentsUI/res/drawable/item_doc_grid_overlay_icon.xml
index 83e4d7e..d40de1e 100644
--- a/packages/DocumentsUI/res/drawable/item_activated_overlay.xml
+++ b/packages/DocumentsUI/res/drawable/item_doc_grid_overlay_icon.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
+<!-- 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.
@@ -14,12 +14,8 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true" android:state_activated="true">
-        <color android:color="@color/accent_color_overlay" />
-    </item>
-    <item android:state_focused="false" android:state_activated="true">
-        <color android:color="@color/accent_color_overlay" />
-    </item>
+<selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="true">
+    <item android:state_focused="true" android:state_activated="true" android:drawable="@drawable/ic_grid_selection_check" />
+    <item android:state_focused="false" android:state_activated="true" android:drawable="@drawable/ic_grid_selection_check" />
     <item android:drawable="@android:color/transparent" />
 </selector>
diff --git a/packages/DocumentsUI/res/drawable/item_activated_overlay.xml b/packages/DocumentsUI/res/drawable/item_doc_list_background.xml
similarity index 84%
rename from packages/DocumentsUI/res/drawable/item_activated_overlay.xml
rename to packages/DocumentsUI/res/drawable/item_doc_list_background.xml
index 83e4d7e..b879542 100644
--- a/packages/DocumentsUI/res/drawable/item_activated_overlay.xml
+++ b/packages/DocumentsUI/res/drawable/item_doc_list_background.xml
@@ -15,11 +15,7 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true" android:state_activated="true">
-        <color android:color="@color/accent_color_overlay" />
-    </item>
-    <item android:state_focused="false" android:state_activated="true">
-        <color android:color="@color/accent_color_overlay" />
-    </item>
+    <item android:state_focused="true" android:state_activated="true" android:drawable="@color/item_doc_list_background_activated" />
+    <item android:state_focused="false" android:state_activated="true" android:drawable="@color/item_doc_list_background_activated" />
     <item android:drawable="@android:color/transparent" />
 </selector>
diff --git a/packages/DocumentsUI/res/drawable/item_activated.xml b/packages/DocumentsUI/res/drawable/item_root_background.xml
similarity index 100%
rename from packages/DocumentsUI/res/drawable/item_activated.xml
rename to packages/DocumentsUI/res/drawable/item_root_background.xml
diff --git a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
index 95af7e9..d124320 100644
--- a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
@@ -17,7 +17,7 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@drawable/item_activated">
+    android:background="@drawable/item_doc_list_background">
 
     <LinearLayout
         android:layout_width="match_parent"
diff --git a/packages/DocumentsUI/res/layout/item_doc_grid.xml b/packages/DocumentsUI/res/layout/item_doc_grid.xml
index bdb3184..d62d050 100644
--- a/packages/DocumentsUI/res/layout/item_doc_grid.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_grid.xml
@@ -17,9 +17,7 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="@dimen/grid_item_height"
-    android:orientation="vertical"
-    android:background="@color/grid_item_background"
-    android:foreground="@drawable/item_activated_overlay">
+    android:background="@color/item_doc_grid_background">
 
     <ImageView
         android:id="@+id/icon_thumb"
@@ -44,7 +42,7 @@
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:background="@drawable/grid_protect_background"
+            android:background="@color/item_doc_grid_protect_background"
             android:orientation="vertical"
             android:paddingStart="16dp"
             android:paddingEnd="12dp"
@@ -68,7 +66,7 @@
                     android:ellipsize="middle"
                     android:textAlignment="viewStart"
                     android:textAppearance="@android:style/TextAppearance.Material.Subhead"
-                    android:textColor="?android:attr/textColorPrimaryInverse" />
+                    android:textColor="@*android:color/primary_text_default_material_dark" />
 
                 <ImageView
                     android:id="@android:id/icon1"
@@ -97,7 +95,7 @@
                     android:ellipsize="end"
                     android:textAlignment="viewStart"
                     android:textAppearance="@android:style/TextAppearance.Material.Caption"
-                    android:textColor="?android:attr/textColorPrimaryInverse" />
+                    android:textColor="@*android:color/primary_text_default_material_dark" />
 
                 <TextView
                     android:id="@+id/size"
@@ -109,7 +107,7 @@
                     android:ellipsize="end"
                     android:textAlignment="viewStart"
                     android:textAppearance="@android:style/TextAppearance.Material.Caption"
-                    android:textColor="?android:attr/textColorPrimaryInverse" />
+                    android:textColor="@*android:color/primary_text_default_material_dark" />
 
                 <ImageView
                     android:id="@android:id/icon2"
@@ -126,4 +124,19 @@
 
     </LinearLayout>
 
+    <ImageView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:src="@drawable/item_doc_grid_overlay"
+        android:contentDescription="@null"
+        android:duplicateParentState="true" />
+
+    <ImageView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:src="@drawable/item_doc_grid_overlay_icon"
+        android:scaleType="center"
+        android:contentDescription="@null"
+        android:duplicateParentState="true" />
+
 </FrameLayout>
diff --git a/packages/DocumentsUI/res/layout/item_doc_list.xml b/packages/DocumentsUI/res/layout/item_doc_list.xml
index c5f1842..c576669 100644
--- a/packages/DocumentsUI/res/layout/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_list.xml
@@ -17,7 +17,7 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@drawable/item_activated">
+    android:background="@drawable/item_doc_list_background">
 
     <LinearLayout
         android:layout_width="match_parent"
diff --git a/packages/DocumentsUI/res/layout/item_root.xml b/packages/DocumentsUI/res/layout/item_root.xml
index 266b9b0..bd83923 100644
--- a/packages/DocumentsUI/res/layout/item_root.xml
+++ b/packages/DocumentsUI/res/layout/item_root.xml
@@ -23,19 +23,21 @@
     android:gravity="center_vertical"
     android:orientation="horizontal"
     android:baselineAligned="false"
-    android:background="@drawable/item_activated">
+    android:background="@drawable/item_root_background">
 
     <FrameLayout
         android:layout_width="@dimen/icon_size"
         android:layout_height="@dimen/icon_size"
-        android:layout_marginEnd="16dp">
+        android:layout_marginEnd="16dp"
+        android:duplicateParentState="true">
 
         <ImageView
             android:id="@android:id/icon"
             android:layout_width="@dimen/root_icon_size"
             android:layout_height="match_parent"
             android:scaleType="centerInside"
-            android:contentDescription="@null" />
+            android:contentDescription="@null"
+            android:duplicateParentState="true" />
 
     </FrameLayout>
 
@@ -54,7 +56,7 @@
             android:ellipsize="end"
             android:textAlignment="viewStart"
             android:textAppearance="@android:style/TextAppearance.Material.Body1"
-            android:textColor="?android:attr/textColorPrimary" />
+            android:textColor="@color/item_root_primary_text" />
 
         <TextView
             android:id="@android:id/summary"
@@ -64,7 +66,7 @@
             android:ellipsize="end"
             android:textAlignment="viewStart"
             android:textAppearance="@android:style/TextAppearance.Material.Body1"
-            android:textColor="?android:attr/textColorSecondary" />
+            android:textColor="@color/item_root_primary_text" />
 
     </LinearLayout>
 
diff --git a/packages/DocumentsUI/res/values/colors.xml b/packages/DocumentsUI/res/values/colors.xml
index 7442b09..2ceb968 100644
--- a/packages/DocumentsUI/res/values/colors.xml
+++ b/packages/DocumentsUI/res/values/colors.xml
@@ -18,8 +18,16 @@
     <color name="material_grey_50">#fffafafa</color>
     <color name="material_grey_300">#ffeeeeee</color>
 
-    <!-- Half-alpha of material_teal_500 -->
-    <color name="accent_color_overlay">#8800bcd4</color>
+    <color name="item_doc_grid_background">@color/material_grey_300</color>
 
-    <color name="grid_item_background">@color/material_grey_300</color>
+    <color name="item_doc_grid_protect_background">#88000000</color>
+
+    <color name="item_doc_grid_overlay_activated">#88000000</color>
+    <color name="item_doc_grid_overlay_disabled">#88ffffff</color>
+
+    <color name="item_doc_list_overlay_disabled">#88ffffff</color>
+
+    <!-- 10% alpha of material_deep_teal_500 -->
+    <color name="item_doc_list_background_activated">#1a009688</color>
+
 </resources>
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index 5cfe046..7693da3 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -28,6 +28,7 @@
         <item name="android:colorAccent">@*android:color/material_deep_teal_500</item>
 
         <item name="android:windowActionBar">false</item>
+        <item name="android:windowActionModeOverlay">true</item>
         <item name="android:windowNoTitle">true</item>
 
         <item name="*android:windowFixedWidthMajor">@null</item>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index caa07ab..39c2252 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -807,6 +807,9 @@
                     || MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, docMimeType);
             final boolean showThumbnail = supportsThumbnail && allowThumbnail && !mSvelteRecents;
 
+            final boolean enabled = isDocumentEnabled(docMimeType, docFlags);
+            final float iconAlpha = (state.derivedMode == MODE_LIST && !enabled) ? 0.5f : 1f;
+
             boolean cacheHit = false;
             if (showThumbnail) {
                 final Uri uri = DocumentsContract.buildDocumentUri(docAuthority, docId);
@@ -817,7 +820,7 @@
                 } else {
                     iconThumb.setImageDrawable(null);
                     final ThumbnailAsyncTask task = new ThumbnailAsyncTask(
-                            uri, iconMime, iconThumb, mThumbSize);
+                            uri, iconMime, iconThumb, mThumbSize, iconAlpha);
                     iconThumb.setTag(task);
                     ProviderExecutor.forAuthority(docAuthority).execute(task);
                 }
@@ -886,7 +889,7 @@
                 // hint to remind user they're a directory.
                 if (Document.MIME_TYPE_DIR.equals(docMimeType) && state.derivedMode == MODE_GRID
                         && showThumbnail) {
-                    iconDrawable = IconUtils.applyTint(context, R.drawable.ic_doc_folder,
+                    iconDrawable = IconUtils.applyTintAttr(context, R.drawable.ic_doc_folder,
                             android.R.attr.textColorPrimaryInverse);
                 }
 
@@ -940,20 +943,12 @@
                 line2.setVisibility(hasLine2 ? View.VISIBLE : View.GONE);
             }
 
-            final boolean enabled = isDocumentEnabled(docMimeType, docFlags);
-            if (enabled) {
-                setEnabledRecursive(convertView, true);
-                iconMime.setAlpha(1f);
-                iconThumb.setAlpha(1f);
-                if (icon1 != null) icon1.setAlpha(1f);
-                if (icon2 != null) icon2.setAlpha(1f);
-            } else {
-                setEnabledRecursive(convertView, false);
-                iconMime.setAlpha(0.5f);
-                iconThumb.setAlpha(0.5f);
-                if (icon1 != null) icon1.setAlpha(0.5f);
-                if (icon2 != null) icon2.setAlpha(0.5f);
-            }
+            setEnabledRecursive(convertView, enabled);
+
+            iconMime.setAlpha(iconAlpha);
+            iconThumb.setAlpha(iconAlpha);
+            if (icon1 != null) icon1.setAlpha(iconAlpha);
+            if (icon2 != null) icon2.setAlpha(iconAlpha);
 
             return convertView;
         }
@@ -1000,14 +995,16 @@
         private final ImageView mIconMime;
         private final ImageView mIconThumb;
         private final Point mThumbSize;
+        private final float mTargetAlpha;
         private final CancellationSignal mSignal;
 
-        public ThumbnailAsyncTask(
-                Uri uri, ImageView iconMime, ImageView iconThumb, Point thumbSize) {
+        public ThumbnailAsyncTask(Uri uri, ImageView iconMime, ImageView iconThumb, Point thumbSize,
+                float targetAlpha) {
             mUri = uri;
             mIconMime = iconMime;
             mIconThumb = iconThumb;
             mThumbSize = thumbSize;
+            mTargetAlpha = targetAlpha;
             mSignal = new CancellationSignal();
         }
 
@@ -1051,11 +1048,10 @@
                 mIconThumb.setTag(null);
                 mIconThumb.setImageBitmap(result);
 
-                final float targetAlpha = mIconMime.isEnabled() ? 1f : 0.5f;
-                mIconMime.setAlpha(targetAlpha);
+                mIconMime.setAlpha(mTargetAlpha);
                 mIconMime.animate().alpha(0f).start();
                 mIconThumb.setAlpha(0f);
-                mIconThumb.animate().alpha(targetAlpha).start();
+                mIconThumb.animate().alpha(mTargetAlpha).start();
             }
         }
     }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
index b2e38fc..416aeb0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
@@ -266,15 +266,16 @@
         }
     }
 
-    public static Drawable applyTint(Context context, int drawableId, int tintAttrId) {
-        final Resources res = context.getResources();
-
-        final TypedValue outValue = new TypedValue();
-        context.getTheme().resolveAttribute(tintAttrId, outValue, true);
-
+    public static Drawable applyTintColor(Context context, int drawableId, int tintColorId) {
         final Drawable icon = context.getDrawable(drawableId);
         icon.mutate();
-        icon.setTintList(res.getColorStateList(outValue.resourceId));
+        icon.setTintList(context.getResources().getColorStateList(tintColorId));
         return icon;
     }
+
+    public static Drawable applyTintAttr(Context context, int drawableId, int tintAttrId) {
+        final TypedValue outValue = new TypedValue();
+        context.getTheme().resolveAttribute(tintAttrId, outValue, true);
+        return applyTintColor(context, drawableId, outValue.resourceId);
+    }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index a465ecd..f81690a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -58,7 +58,7 @@
  * Cache of known storage backends and their roots.
  */
 public class RootsCache {
-    private static final boolean LOGD = true;
+    private static final boolean LOGD = false;
 
     public static final Uri sNotificationUri = Uri.parse(
             "content://com.android.documentsui.roots/");
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index b19e028..884cf31 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -235,7 +235,7 @@
             final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
 
             final Context context = convertView.getContext();
-            icon.setImageDrawable(root.loadIcon(context));
+            icon.setImageDrawable(root.loadDrawerIcon(context));
             title.setText(root.title);
 
             // Show available space if no summary
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index fcfe518..97d8ed0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -211,9 +211,17 @@
         }
     }
 
+    public Drawable loadDrawerIcon(Context context) {
+        if (derivedIcon != 0) {
+            return IconUtils.applyTintColor(context, derivedIcon, R.color.item_root_icon);
+        } else {
+            return IconUtils.loadPackageIcon(context, authority, icon);
+        }
+    }
+
     public Drawable loadGridIcon(Context context) {
         if (derivedIcon != 0) {
-            return IconUtils.applyTint(context, derivedIcon,
+            return IconUtils.applyTintAttr(context, derivedIcon,
                     android.R.attr.textColorPrimaryInverse);
         } else {
             return IconUtils.loadPackageIcon(context, authority, icon);
@@ -222,7 +230,7 @@
 
     public Drawable loadToolbarIcon(Context context) {
         if (derivedIcon != 0) {
-            return IconUtils.applyTint(context, derivedIcon,
+            return IconUtils.applyTintAttr(context, derivedIcon,
                     android.R.attr.colorControlNormal);
         } else {
             return IconUtils.loadPackageIcon(context, authority, icon);
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index ee5d42a..761d58a 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -27,6 +27,7 @@
         android:id="@+id/static_content"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
+        android:paddingStart="8dip"
         android:elevation="@dimen/preview_controls_elevation"
         android:background="?android:attr/colorPrimary">
 
diff --git a/packages/PrintSpooler/res/layout/print_activity_controls.xml b/packages/PrintSpooler/res/layout/print_activity_controls.xml
index 0629481..c2a0da9 100644
--- a/packages/PrintSpooler/res/layout/print_activity_controls.xml
+++ b/packages/PrintSpooler/res/layout/print_activity_controls.xml
@@ -55,8 +55,7 @@
                     android:text="@string/label_copies">
                 </TextView>
 
-                <view
-                    class="com.android.printspooler.widget.FirstFocusableEditText"
+                <EditText
                     android:id="@+id/copies_edittext"
                     android:layout_width="fill_parent"
                     android:layout_height="wrap_content"
@@ -64,7 +63,7 @@
                     android:singleLine="true"
                     android:ellipsize="end"
                     android:inputType="numberDecimal">
-                </view>
+                </EditText>
 
             </LinearLayout>
 
@@ -198,8 +197,7 @@
                     android:visibility="visible">
                 </TextView>
 
-                <view
-                    class="com.android.printspooler.widget.FirstFocusableEditText"
+                <EditText
                     android:id="@+id/page_range_edittext"
                     android:layout_width="fill_parent"
                     android:layout_height="wrap_content"
@@ -208,7 +206,7 @@
                     android:ellipsize="end"
                     android:visibility="visible"
                     android:inputType="textNoSuggestions">
-                </view>
+                </EditText>
 
             </LinearLayout>
 
diff --git a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
index 43d8aaf..4381a7a 100644
--- a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
+++ b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
@@ -17,8 +17,8 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
-      android:paddingStart="16dip"
-      android:paddingEnd="16dip"
+      android:paddingStart="8dip"
+      android:paddingEnd="8dip"
       android:minHeight="56dip"
       android:orientation="horizontal"
       android:gravity="start|center_vertical">
diff --git a/packages/PrintSpooler/res/layout/select_printer_activity.xml b/packages/PrintSpooler/res/layout/select_printer_activity.xml
index 173057b..77c500a 100644
--- a/packages/PrintSpooler/res/layout/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/layout/select_printer_activity.xml
@@ -23,8 +23,6 @@
         android:id="@android:id/list"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"
-        android:paddingStart="@dimen/printer_list_view_padding_start"
-        android:paddingEnd="@dimen/printer_list_view_padding_end"
         android:scrollbarStyle="outsideOverlay"
         android:cacheColorHint="@android:color/transparent"
         android:scrollbarAlwaysDrawVerticalTrack="true" >
diff --git a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml b/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
deleted file mode 100644
index 14403a1..0000000
--- a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="wrap_content"
-    android:minHeight="?android:attr/listPreferredItemHeightSmall"
-    android:orientation="vertical"
-    android:gravity="start|center_vertical">
-
-    <TextView
-        android:id="@+id/title"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        style="?android:attr/spinnerDropDownItemStyle"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textColor="?android:attr/textColorPrimary"
-        android:singleLine="true"
-        android:ellipsize="end"
-        android:textIsSelectable="false"
-        android:gravity="top|left"
-        android:duplicateParentState="true">
-    </TextView>
-
-    <TextView
-        android:id="@+id/subtitle"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        style="?android:attr/spinnerDropDownItemStyle"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textColor="?android:attr/textColorPrimary"
-        android:singleLine="true"
-        android:ellipsize="end"
-        android:textIsSelectable="false"
-        android:visibility="gone"
-        android:duplicateParentState="true">
-    </TextView>
-
-</LinearLayout>
diff --git a/packages/PrintSpooler/res/values-land/constants.xml b/packages/PrintSpooler/res/values-land/constants.xml
index a4666a5..6cf9754b5 100644
--- a/packages/PrintSpooler/res/values-land/constants.xml
+++ b/packages/PrintSpooler/res/values-land/constants.xml
@@ -16,10 +16,6 @@
 
 <resources>
 
-    <dimen name="printer_list_view_padding_start">48dip</dimen>
-
-    <dimen name="printer_list_view_padding_end">48dip</dimen>
-
     <integer name="preview_page_per_row_count">2</integer>
 
     <integer name="print_option_column_count">3</integer>
diff --git a/packages/PrintSpooler/res/values/constants.xml b/packages/PrintSpooler/res/values/constants.xml
index b95703b..b4e4777 100644
--- a/packages/PrintSpooler/res/values/constants.xml
+++ b/packages/PrintSpooler/res/values/constants.xml
@@ -28,9 +28,6 @@
 
     <dimen name="print_dialog_frame_max_width_dip">400dip</dimen>
 
-    <dimen name="printer_list_view_padding_start">16dip</dimen>
-    <dimen name="printer_list_view_padding_end">16dip</dimen>
-
     <dimen name="selected_page_elevation">6dip</dimen>
     <dimen name="unselected_page_elevation">2dip</dimen>
 
@@ -46,6 +43,6 @@
     <fraction name="page_unselected_alpha">50%</fraction>
 
     <dimen name="preview_page_footer_height">32dip</dimen>
-    <dimen name="preview_page_min_width">130dip</dimen>
+    <dimen name="preview_page_min_width">128dip</dimen>
 
 </resources>
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index db319e9..532b01f 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -16,7 +16,10 @@
 
 <resources>
 
-    <style name="PrintActivity" parent="@android:style/Theme.Material.Settings">
+    <style name="PrintActivity" parent="@android:style/Theme.Material">
+        <item name="android:colorPrimary">@*android:color/material_blue_grey_900</item>
+        <item name="android:colorPrimaryDark">@*android:color/material_blue_grey_950</item>
+        <item name="android:colorAccent">@*android:color/material_deep_teal_500</item>
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowContentOverlay">@null</item>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
index 5bcdb9f..d949673 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
@@ -289,15 +289,11 @@
                     + " for position: " + position);
         }
 
-        final int pageCount = getItemCount();
         MyViewHolder myHolder = (MyViewHolder) holder;
 
         View page = holder.itemView;
-        if (pageCount > 1) {
-            page.setOnClickListener(mPageClickListener);
-        } else {
-            page.setOnClickListener(null);
-        }
+        page.setOnClickListener(mPageClickListener);
+
         page.setTag(holder);
 
         myHolder.mPageInAdapter = position;
@@ -339,16 +335,9 @@
         }
         content.init(provider, mMediaSize, mMinMargins);
 
-
         View pageSelector = page.findViewById(R.id.page_selector);
         pageSelector.setTag(myHolder);
-        if (pageCount > 1) {
-            pageSelector.setOnClickListener(mPageClickListener);
-            pageSelector.setVisibility(View.VISIBLE);
-        } else {
-            pageSelector.setOnClickListener(null);
-            pageSelector.setVisibility(View.GONE);
-        }
+        pageSelector.setOnClickListener(mPageClickListener);
 
         if (mConfirmedPagesInDocument.indexOfKey(pageInDocument) >= 0) {
             pageSelector.setSelected(true);
@@ -449,8 +438,9 @@
 
         final int verticalPadding;
         if (mPageContentHeight + mFooterHeight + mPreviewListPadding > availableHeight) {
-            verticalPadding = Math.max(mPreviewPageMargin,
-                    (availableHeight - totalContentHeight) / 2);
+            verticalPadding = Math.max(0,
+                    (availableHeight - mPageContentHeight - mFooterHeight) / 2
+                            - mPreviewPageMargin);
         } else {
             verticalPadding = Math.max(mPreviewListPadding,
                     (availableHeight - totalContentHeight) / 2);
@@ -791,6 +781,9 @@
                 page.animate().translationZ(mSelectedPageElevation)
                         .alpha(mSelectedPageAlpha);
             } else {
+                if (mConfirmedPagesInDocument.size() <= 1) {
+                    return;
+                }
                 mConfirmedPagesInDocument.remove(pageInDocument);
                 pageSelector.setSelected(false);
                 page.animate().translationZ(mUnselectedPageElevation)
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index fe17516..01c9746 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -982,21 +982,21 @@
 
         // Media size.
         mMediaSizeSpinnerAdapter = new ArrayAdapter<>(
-                this, R.layout.spinner_dropdown_item, R.id.title);
+                this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
         mMediaSizeSpinner = (Spinner) findViewById(R.id.paper_size_spinner);
         mMediaSizeSpinner.setAdapter(mMediaSizeSpinnerAdapter);
         mMediaSizeSpinner.setOnItemSelectedListener(itemSelectedListener);
 
         // Color mode.
         mColorModeSpinnerAdapter = new ArrayAdapter<>(
-                this, R.layout.spinner_dropdown_item, R.id.title);
+                this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
         mColorModeSpinner = (Spinner) findViewById(R.id.color_spinner);
         mColorModeSpinner.setAdapter(mColorModeSpinnerAdapter);
         mColorModeSpinner.setOnItemSelectedListener(itemSelectedListener);
 
         // Orientation
         mOrientationSpinnerAdapter = new ArrayAdapter<>(
-                this, R.layout.spinner_dropdown_item, R.id.title);
+                this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
         String[] orientationLabels = getResources().getStringArray(
                 R.array.orientation_labels);
         mOrientationSpinnerAdapter.add(new SpinnerItem<>(
@@ -1008,8 +1008,8 @@
         mOrientationSpinner.setOnItemSelectedListener(itemSelectedListener);
 
         // Range options
-        ArrayAdapter<SpinnerItem<Integer>> rangeOptionsSpinnerAdapter =
-                new ArrayAdapter<>(this, R.layout.spinner_dropdown_item, R.id.title);
+        ArrayAdapter<SpinnerItem<Integer>> rangeOptionsSpinnerAdapter = new ArrayAdapter<>(
+                this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
         mRangeOptionsSpinner = (Spinner) findViewById(R.id.range_options_spinner);
         mRangeOptionsSpinner.setAdapter(rangeOptionsSpinnerAdapter);
         mRangeOptionsSpinner.setOnItemSelectedListener(itemSelectedListener);
@@ -1075,6 +1075,7 @@
                 mDestinationSpinner.setEnabled(false);
             }
             mCopiesEditText.setEnabled(false);
+            mCopiesEditText.setFocusable(false);
             mMediaSizeSpinner.setEnabled(false);
             mColorModeSpinner.setEnabled(false);
             mOrientationSpinner.setEnabled(false);
@@ -1089,6 +1090,7 @@
         // available, we disable all print options except the destination.
         if (mCurrentPrinter == null || !canPrint(mCurrentPrinter)) {
             mCopiesEditText.setEnabled(false);
+            mCopiesEditText.setFocusable(false);
             mMediaSizeSpinner.setEnabled(false);
             mColorModeSpinner.setEnabled(false);
             mOrientationSpinner.setEnabled(false);
@@ -1316,8 +1318,10 @@
         // Copies
         if (mDestinationSpinnerAdapter.getPdfPrinter() != mCurrentPrinter) {
             mCopiesEditText.setEnabled(true);
+            mCopiesEditText.setFocusableInTouchMode(true);
         } else {
             mCopiesEditText.setEnabled(false);
+            mCopiesEditText.setFocusable(false);
         }
         if (mCopiesEditText.getError() == null
                 && TextUtils.isEmpty(mCopiesEditText.getText())) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/FirstFocusableEditText.java b/packages/PrintSpooler/src/com/android/printspooler/widget/FirstFocusableEditText.java
deleted file mode 100644
index d6bb7c8..0000000
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/FirstFocusableEditText.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.printspooler.widget;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.widget.EditText;
-
-/**
- * An instance of this class class is intended to be the first focusable
- * in a layout to which the system automatically gives focus. It performs
- * some voodoo to avoid the first tap on it to start an edit mode, rather
- * to bring up the IME, i.e. to get the behavior as if the view was not
- * focused.
- */
-public final class FirstFocusableEditText extends EditText {
-    private boolean mClickedBeforeFocus;
-    private CharSequence mError;
-
-    public FirstFocusableEditText(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    public boolean performClick() {
-        super.performClick();
-        if (isFocused() && !mClickedBeforeFocus) {
-            clearFocus();
-            requestFocus();
-        }
-        mClickedBeforeFocus = true;
-        return true;
-    }
-
-    @Override
-    public CharSequence getError() {
-        return mError;
-    }
-
-    @Override
-    public void setError(CharSequence error, Drawable icon) {
-        setCompoundDrawables(null, null, icon, null);
-        mError = error;
-    }
-
-    protected void onFocusChanged(boolean gainFocus, int direction,
-            Rect previouslyFocusedRect) {
-        if (!gainFocus) {
-            mClickedBeforeFocus = false;
-        }
-        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-    }
-}
\ No newline at end of file
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
index e428948..c84b06a 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
@@ -152,6 +152,17 @@
 
         // Make sure we start in a closed options state.
         onDragProgress(1.0f);
+
+        // The framework gives focus to the frist focusable and we
+        // do not want that, hence we will take focus instead.
+        setFocusableInTouchMode(true);
+    }
+
+    @Override
+    public void focusableViewAvailable(View v) {
+        // The framework gives focus to the frist focusable and we
+        // do not want that, hence do not announce new focusables.
+        return;
     }
 
     @Override
@@ -309,6 +320,7 @@
             mSummaryContent.setLayerType(View.LAYER_TYPE_NONE, null);
             mDraggableContent.setLayerType(View.LAYER_TYPE_NONE, null);
             mMoreOptionsButton.setLayerType(View.LAYER_TYPE_NONE, null);
+            mMoreOptionsButton.setLayerType(View.LAYER_TYPE_NONE, null);
         }
 
         mDragProgress = progress;
@@ -320,7 +332,6 @@
         mMoreOptionsButton.setAlpha(inverseAlpha);
 
         mEmbeddedContentScrim.setBackgroundColor(computeScrimColor());
-
         if (progress == 0) {
             if (mOptionsStateChangeListener != null) {
                 mOptionsStateChangeListener.onOptionsOpened();
@@ -354,14 +365,15 @@
     }
 
     private void ensureImeClosedAndInputFocusCleared() {
-        View focus = findFocus();
-        if (focus != null) {
+        View focused = findFocus();
+
+        if (focused != null && focused.isFocused()) {
             InputMethodManager imm = (InputMethodManager) mContext.getSystemService(
                     Context.INPUT_METHOD_SERVICE);
-            if (imm.isActive(focus)) {
+            if (imm.isActive(focused)) {
                 imm.hideSoftInputFromWindow(getWindowToken(), 0);
             }
-            focus.clearFocus();
+            focused.clearFocus();
         }
     }
 
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 299e50c..a3bed4f 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -187,6 +187,9 @@
     <!-- Default for Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1==on -->
     <integer name="def_lock_screen_show_notifications">1</integer>
 
+    <!-- Default for Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS -->
+    <bool name="def_lock_screen_allow_private_notifications">true</bool>
+
     <!-- Default for Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, 1==on -->
     <integer name="def_heads_up_enabled">1</integer>
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index edefb13..fd5e6fe 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -70,7 +70,7 @@
     // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
     // is properly propagated through your change.  Not doing so will result in a loss of user
     // settings.
-    private static final int DATABASE_VERSION = 108;
+    private static final int DATABASE_VERSION = 109;
 
     private Context mContext;
     private int mUserHandle;
@@ -1673,8 +1673,8 @@
             try {
                 stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
                         + " VALUES(?,?);");
-                loadBooleanSetting(stmt, Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
-                        R.bool.def_guest_user_enabled);
+                loadIntegerSetting(stmt, Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+                        R.integer.def_lock_screen_show_notifications);
                 if (mUserHandle == UserHandle.USER_OWNER) {
                     final int oldShow = getIntValueFromTable(db,
                             TABLE_GLOBAL, Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, -1);
@@ -1733,6 +1733,22 @@
             upgradeVersion = 108;
         }
 
+        if (upgradeVersion < 109) {
+            db.beginTransaction();
+            SQLiteStatement stmt = null;
+            try {
+                stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
+                        + " VALUES(?,?);");
+                loadBooleanSetting(stmt, Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+                        R.bool.def_lock_screen_allow_private_notifications);
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+                if (stmt != null) stmt.close();
+            }
+            upgradeVersion = 109;
+        }
+
         // *** Remember to update DATABASE_VERSION above!
 
         if (upgradeVersion != currentVersion) {
@@ -2301,6 +2317,9 @@
             loadIntegerSetting(stmt, Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
                     R.integer.def_lock_screen_show_notifications);
 
+            loadBooleanSetting(stmt, Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+                    R.bool.def_lock_screen_allow_private_notifications);
+
         } finally {
             if (stmt != null) stmt.close();
         }
diff --git a/packages/SystemUI/res/drawable/ic_android.xml b/packages/SystemUI/res/drawable/ic_android.xml
new file mode 100644
index 0000000..19ee9a7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_android.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M6.000000,18.000000c0.000000,0.600000 0.400000,1.000000 1.000000,1.000000l1.000000,0.000000l0.000000,3.500000C8.000000,23.299999 8.700000,24.000000 9.500000,24.000000c0.800000,0.000000 1.500000,-0.700000 1.500000,-1.500000L11.000000,19.000000l2.000000,0.000000l0.000000,3.500000c0.000000,0.800000 0.700000,1.500000 1.500000,1.500000c0.800000,0.000000 1.500000,-0.700000 1.500000,-1.500000L16.000000,19.000000l1.000000,0.000000c0.600000,0.000000 1.000000,-0.400000 1.000000,-1.000000L18.000000,8.000000L6.000000,8.000000L6.000000,18.000000zM3.500000,8.000000C2.700000,8.000000 2.000000,8.700000 2.000000,9.500000l0.000000,7.000000C2.000000,17.299999 2.700000,18.000000 3.500000,18.000000C4.300000,18.000000 5.000000,17.299999 5.000000,16.500000l0.000000,-7.000000C5.000000,8.700000 4.300000,8.000000 3.500000,8.000000zM20.500000,8.000000C19.700001,8.000000 19.000000,8.700000 19.000000,9.500000l0.000000,7.000000c0.000000,0.800000 0.700000,1.500000 1.500000,1.500000c0.800000,0.000000 1.500000,-0.700000 1.500000,-1.500000l0.000000,-7.000000C22.000000,8.700000 21.299999,8.000000 20.500000,8.000000zM15.500000,2.200000l1.300000,-1.300000c0.200000,-0.200000 0.200000,-0.500000 0.000000,-0.700000c-0.200000,-0.200000 -0.500000,-0.200000 -0.700000,0.000000l-1.500000,1.500000C13.900000,1.200000 13.000000,1.000000 12.000000,1.000000c-1.000000,0.000000 -1.900000,0.200000 -2.700000,0.600000L7.900000,0.100000C7.700000,0.000000 7.300000,0.000000 7.100000,0.100000C7.000000,0.300000 7.000000,0.700000 7.100000,0.900000l1.300000,1.300000C7.000000,3.300000 6.000000,5.000000 6.000000,7.000000l12.000000,0.000000C18.000000,5.000000 17.000000,3.200000 15.500000,2.200000zM10.000000,5.000000L9.000000,5.000000L9.000000,4.000000l1.000000,0.000000L10.000000,5.000000zM15.000000,5.000000l-1.000000,0.000000L14.000000,4.000000l1.000000,0.000000L15.000000,5.000000z"
+        android:fillColor="#ffffffff"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_close.xml b/packages/SystemUI/res/drawable/ic_close.xml
new file mode 100644
index 0000000..7d93d45
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_close.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
+        android:fillColor="#FF000000"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index ef0c9bb..ca07c87 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -67,6 +67,6 @@
         android:src="@drawable/ic_lock_24dp"
         android:scaleType="center"
         android:tint="#ffffffff"
-        android:contentDescription="@string/accessibility_unlock_button_not_secured" />
+        android:contentDescription="@string/accessibility_unlock_button" />
 
 </com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
index eff3758..351177b 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
@@ -38,8 +38,8 @@
             android:id="@+id/more_text"
             android:layout_width="32dp"
             android:layout_height="32dp"
-            android:layout_marginStart="20dp"
-            android:layout_marginEnd="16dp"
+            android:layout_marginStart="16dp"
+            android:layout_marginEnd="12dp"
             android:layout_gravity="center_vertical"
             android:background="@drawable/keyguard_overflow_number_background"
             android:gravity="center"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b000a48..b488c56 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -213,6 +213,8 @@
     <string name="accessibility_camera_button">Camera</string>
     <!-- Content description of the phone button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_phone_button">Phone</string>
+    <!-- Content description of the unlock button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_unlock_button">Unlock</string>
     <!-- Click action label for accessibility for the unlock button. [CHAR LIMIT=NONE] -->
     <string name="unlock_label">unlock</string>
     <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
@@ -220,17 +222,6 @@
     <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
     <string name="camera_label">open camera</string>
 
-    <!-- Content description of the lock icon when device is secured (lock closed) and trust not managed (not shown on the screen). [CHAR LIMIT=NONE] -->
-    <string name="accessibility_unlock_button_secured">Device secured.</string>
-    <!-- Content description of the lock icon when device is not secured (lock open) and trust not managed (not shown on the screen). [CHAR LIMIT=NONE] -->
-    <string name="accessibility_unlock_button_not_secured">Device not secured.</string>
-    <!-- Content description of the lock icon when device is secured (lock closed) and trust managed (not shown on the screen). [CHAR LIMIT=NONE] -->
-    <string name="accessibility_unlock_button_secured_trust_managed">Device secured, trust agent active.</string>
-    <!-- Content description of the lock icon when device is not secured (lock open) and trust managed (not shown on the screen). [CHAR LIMIT=NONE] -->
-    <string name="accessibility_unlock_button_not_secured_trust_managed">Device not secured, trust agent active.</string>
-    <!-- Content description of the lock icon when face unlock is running (face icon) and trust managed (not shown on the screen). [CHAR LIMIT=NONE] -->
-    <string name="accessibility_unlock_button_face_unlock_running">Face detection running, trust agent active.</string>
-
     <!-- Content description of the switch input method button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_ime_switch_button">Switch input method button.</string>
     <!-- Content description of the compatibility zoom button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -323,6 +314,15 @@
     <!-- Content description of an item with full signal for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_signal_full">Signal full.</string>
 
+    <!-- Content description of an item that is turned on for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_desc_on">On.</string>
+    <!-- Content description of an item that is turned off for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_desc_off">Off.</string>
+    <!-- Content description of an item that is connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_desc_connected">Connected.</string>
+    <!-- Content description of an item that is connecting for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_desc_connecting">Connecting.</string>
+
     <!-- Content description of the data connection type GPRS for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_data_connection_gprs">GPRS</string>
 
@@ -894,6 +894,18 @@
     <!-- Indication on the keyguard that appears when the user disables trust agents until the next time they unlock manually. [CHAR LIMIT=NONE] -->
     <string name="keyguard_indication_trust_disabled">Device will stay locked until you manually unlock</string>
 
+    <!-- Title of notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=40] -->
+    <string name="hidden_notifications_title">Get notifications faster</string>
+
+    <!-- Body of notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=60] -->
+    <string name="hidden_notifications_text">See them before you unlock</string>
+
+    <!-- Cancel action for notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=10] -->
+    <string name="hidden_notifications_cancel">No thanks</string>
+
+    <!-- continue action for notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=10] -->
+    <string name="hidden_notifications_setup">Set up</string>
+
     <!-- Indication that the current volume and other effects (vibration) are being suppressed by a third party, such as a notification listener. [CHAR LIMIT=30] -->
     <string name="muted_by">Muted by <xliff:g id="third_party">%1$s</xliff:g></string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 1ac3bc3..0c6e7b6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -67,6 +67,7 @@
     ArrayList<TaskStack> mStacks;
     View mSearchBar;
     RecentsViewCallbacks mCb;
+    boolean mAlreadyLaunchingTask;
 
     public RecentsView(Context context) {
         super(context);
@@ -120,6 +121,9 @@
             }
             addView(stackView);
         }
+
+        // Reset the launched state
+        mAlreadyLaunchingTask = false;
     }
 
     /** Removes all the task stack views from this recents view. */
@@ -381,6 +385,11 @@
         if (mCb != null) {
             mCb.onTaskViewClicked();
         }
+        // Skip if we are already launching tasks
+        if (mAlreadyLaunchingTask) {
+            return;
+        }
+        mAlreadyLaunchingTask = true;
 
         // Upfront the processing of the thumbnail
         TaskViewTransform transform = new TaskViewTransform();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index da99118..f04c8c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -22,6 +22,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.TaskStackBuilder;
 import android.app.admin.DevicePolicyManager;
@@ -35,7 +36,11 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.Build;
@@ -78,6 +83,7 @@
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.statusbar.StatusBarIconList;
 import com.android.internal.util.NotificationColorUtil;
+import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.SearchPanelView;
@@ -126,6 +132,12 @@
     public static final int EXPANDED_LEAVE_ALONE = -10000;
     public static final int EXPANDED_FULL_OPEN = -10001;
 
+    private static final int HIDDEN_NOTIFICATION_ID = 10000;
+    private static final String BANNER_ACTION_CANCEL =
+            "com.android.systemui.statusbar.banner_action_cancel";
+    private static final String BANNER_ACTION_SETUP =
+            "com.android.systemui.statusbar.banner_action_setup";
+
     protected CommandQueue mCommandQueue;
     protected IStatusBarService mBarService;
     protected H mHandler = createHandler();
@@ -308,6 +320,20 @@
                 mUsersAllowingPrivateNotifications.clear();
                 updateLockscreenNotificationSetting();
                 updateNotifications();
+            } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
+                NotificationManager noMan = (NotificationManager)
+                        mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+                noMan.cancel(HIDDEN_NOTIFICATION_ID);
+
+                Settings.Secure.putInt(mContext.getContentResolver(),
+                        Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
+                if (BANNER_ACTION_SETUP.equals(action)) {
+                    animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
+                    mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
+                            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+
+                    );
+                }
             }
         }
     };
@@ -490,12 +516,61 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_USER_SWITCHED);
         filter.addAction(Intent.ACTION_USER_ADDED);
+        filter.addAction(BANNER_ACTION_CANCEL);
+        filter.addAction(BANNER_ACTION_SETUP);
         filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
         mContext.registerReceiver(mBroadcastReceiver, filter);
 
         updateCurrentProfilesCache();
     }
 
+    protected void notifyUserAboutHiddenNotifications() {
+        if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
+            Log.d(TAG, "user hasn't seen notification about hidden notifications");
+            final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
+            if (!lockPatternUtils.isSecure()) {
+                Log.d(TAG, "insecure lockscreen, skipping notification");
+                Settings.Secure.putInt(mContext.getContentResolver(),
+                        Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
+                return;
+            }
+            Log.d(TAG, "disabling lockecreen notifications and alerting the user");
+            // disable lockscreen notifications until user acts on the banner.
+            Settings.Secure.putInt(mContext.getContentResolver(),
+                    Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
+
+            final String packageName = mContext.getPackageName();
+            PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0,
+                    new Intent(BANNER_ACTION_CANCEL).setPackage(packageName),
+                    PendingIntent.FLAG_CANCEL_CURRENT);
+            PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0,
+                    new Intent(BANNER_ACTION_SETUP).setPackage(packageName),
+                    PendingIntent.FLAG_CANCEL_CURRENT);
+
+            final Resources res = mContext.getResources();
+            final int colorRes = com.android.internal.R.color.system_notification_accent_color;
+            Notification.Builder note = new Notification.Builder(mContext)
+                    .setSmallIcon(R.drawable.ic_android)
+                    .setContentTitle(mContext.getString(R.string.hidden_notifications_title))
+                    .setContentText(mContext.getString(R.string.hidden_notifications_text))
+                    .setPriority(Notification.PRIORITY_HIGH)
+                    .setOngoing(true)
+                    .setColor(res.getColor(colorRes))
+                    .setContentIntent(setupIntent)
+                    .addAction(R.drawable.ic_close,
+                            mContext.getString(R.string.hidden_notifications_cancel),
+                            cancelIntent)
+                    .addAction(R.drawable.ic_settings,
+                            mContext.getString(R.string.hidden_notifications_setup),
+                            setupIntent);
+
+            NotificationManager noMan =
+                    (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+            noMan.notify(HIDDEN_NOTIFICATION_ID, note.build());
+        }
+    }
+
     public void userSwitched(int newUserId) {
         // should be overridden
     }
@@ -582,9 +657,11 @@
 
     protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
             NotificationData.Entry entry) {
+        PackageManager pmUser = getPackageManagerForUser(
+                entry.notification.getUser().getIdentifier());
         int version = 0;
         try {
-            ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.getPackageName(), 0);
+            ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
             version = info.targetSdkVersion;
         } catch (NameNotFoundException ex) {
             Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 62552b2..f9da30f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -380,21 +380,10 @@
         mLockIcon.setImageResource(iconRes);
         boolean trustManaged = mUnlockMethodCache.isTrustManaged();
         mTrustDrawable.setTrustManaged(trustManaged);
-
         updateLockIconClickability();
-        updateLockIconContentDescription(mUnlockMethodCache.isFaceUnlockRunning(),
-                mUnlockMethodCache.isMethodInsecure(), trustManaged);
     }
 
-    private void updateLockIconContentDescription(boolean faceUnlockRunning, boolean insecure,
-            boolean trustManaged) {
-        mLockIcon.setContentDescription(getResources().getString(
-                faceUnlockRunning ? R.string.accessibility_unlock_button_face_unlock_running
-                : insecure && !trustManaged ? R.string.accessibility_unlock_button_not_secured
-                : insecure ? R.string.accessibility_unlock_button_not_secured_trust_managed
-                : !trustManaged ? R.string.accessibility_unlock_button_secured
-                : R.string.accessibility_unlock_button_secured_trust_managed));
-    }
+
 
     public KeyguardAffordanceView getPhoneView() {
         return mPhoneImageView;
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 9fd3d9c..1a0d2d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -582,6 +582,8 @@
         putComponent(PhoneStatusBar.class, this);
 
         setControllerUsers();
+
+        notifyUserAboutHiddenNotifications();
     }
 
     // ================================================================================
@@ -3612,7 +3614,8 @@
     }
 
     public boolean onSpacePressed() {
-        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
+        if (mScreenOn != null && mScreenOn
+                && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
             animateCollapsePanels(0 /* flags */, true /* force */);
             return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index f03c5eb..12c887e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -26,7 +26,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ServiceInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -46,7 +45,6 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.Vibrator;
-import android.provider.Settings.Global;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.KeyEvent;
@@ -58,6 +56,7 @@
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.ImageView;
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
@@ -97,6 +96,7 @@
     private static final int TIMEOUT_DELAY_SHORT = 1500;
     private static final int TIMEOUT_DELAY_COLLAPSED = 4500;
     private static final int TIMEOUT_DELAY_SAFETY_WARNING = 5000;
+    private static final int TIMEOUT_DELAY_SAFETY_WARNING_TALKBACK = 25000;
     private static final int TIMEOUT_DELAY_EXPANDED = 10000;
 
     private static final int MSG_VOLUME_CHANGED = 0;
@@ -161,6 +161,7 @@
     private int mActiveStreamType = -1;
     /** All the slider controls mapped by stream type */
     private SparseArray<StreamControl> mStreamControls;
+    private final AccessibilityManager mAccessibilityManager;
 
     private enum StreamResources {
         BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
@@ -332,6 +333,8 @@
         mContext = context;
         mZenController = zenController;
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        mAccessibilityManager = (AccessibilityManager) context.getSystemService(
+                Context.ACCESSIBILITY_SERVICE);
 
         // For now, only show master volume if master volume is supported
         final Resources res = context.getResources();
@@ -791,7 +794,8 @@
     }
 
     private void updateTimeoutDelay() {
-        mTimeoutDelay = sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
+        mTimeoutDelay = sSafetyWarning != null ? mAccessibilityManager.isEnabled() ?
+                TIMEOUT_DELAY_SAFETY_WARNING_TALKBACK : TIMEOUT_DELAY_SAFETY_WARNING
                 : mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
                 : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
                 : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
@@ -1214,6 +1218,7 @@
             }
             updateStates();
         }
+        updateTimeoutDelay();
         resetTimeout();
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java b/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java
index 71b0d53..6f79f58 100644
--- a/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java
+++ b/policy/src/com/android/internal/policy/impl/EnableAccessibilityController.java
@@ -83,6 +83,7 @@
 
 
     private final Context mContext;
+    private final Runnable mOnAccessibilityEnabledCallback;
     private final UserManager mUserManager;
     private final TextToSpeech mTts;
     private final Ringtone mTone;
@@ -97,8 +98,9 @@
     private float mSecondPointerDownX;
     private float mSecondPointerDownY;
 
-    public EnableAccessibilityController(Context context) {
+    public EnableAccessibilityController(Context context, Runnable onAccessibilityEnabledCallback) {
         mContext = context;
+        mOnAccessibilityEnabledCallback = onAccessibilityEnabledCallback;
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         mTts = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
             @Override
@@ -275,5 +277,7 @@
                 /* ignore */
             }
         }
+
+        mOnAccessibilityEnabledCallback.run();
     }
 }
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index ae94654..41695c1 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -1073,7 +1073,13 @@
             // is dismissed on the first down while the global gesture is a long press
             // with two fingers anywhere on the screen.
             if (EnableAccessibilityController.canEnableAccessibilityViaGesture(mContext)) {
-                mEnableAccessibilityController = new EnableAccessibilityController(mContext);
+                mEnableAccessibilityController = new EnableAccessibilityController(mContext,
+                        new Runnable() {
+                    @Override
+                    public void run() {
+                        dismiss();
+                    }
+                });
                 super.setCanceledOnTouchOutside(false);
             } else {
                 mEnableAccessibilityController = null;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index a13da609..93591a9 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -2754,11 +2754,13 @@
                 mStatusColorView = updateColorViewInt(mStatusColorView,
                         SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
                         mStatusBarColor, mLastTopInset, Gravity.TOP,
-                        STATUS_BAR_BACKGROUND_TRANSITION_NAME);
+                        STATUS_BAR_BACKGROUND_TRANSITION_NAME,
+                        com.android.internal.R.id.statusBarBackground);
                 mNavigationColorView = updateColorViewInt(mNavigationColorView,
                         SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
                         mNavigationBarColor, mLastBottomInset, Gravity.BOTTOM,
-                        NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME);
+                        NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
+                        com.android.internal.R.id.navigationBarBackground);
             }
             if (insets != null) {
                 insets = insets.consumeStableInsets();
@@ -2767,7 +2769,7 @@
         }
 
         private View updateColorViewInt(View view, int systemUiHideFlag, int translucentFlag,
-                int color, int height, int verticalGravity, String transitionName) {
+                int color, int height, int verticalGravity, String transitionName, int id) {
             boolean show = height > 0 && (mLastSystemUiVisibility & systemUiHideFlag) == 0
                     && (getAttributes().flags & translucentFlag) == 0
                     && (color & Color.BLACK) != 0
@@ -2778,6 +2780,7 @@
                     view = new View(mContext);
                     view.setBackgroundColor(color);
                     view.setTransitionName(transitionName);
+                    view.setId(id);
                     addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, height,
                             Gravity.START | verticalGravity));
                 }
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index ac0ca0a..af5c13d 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -708,6 +708,7 @@
                      // Send an event to the end of the drag gesture.
                      sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
                  }
+                 mCurrentState = STATE_TOUCH_EXPLORING;
             } break;
             case MotionEvent.ACTION_UP: {
                 mAms.onTouchInteractionEnd();
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 86ce961..92f5170 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -16,9 +16,12 @@
 
 package com.android.server;
 
+import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 
@@ -31,6 +34,7 @@
 import android.os.Binder;
 import android.os.Environment;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.storage.IMountService;
 import android.os.ServiceManager;
@@ -41,11 +45,14 @@
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.provider.Settings.SettingNotFoundException;
+import android.security.KeyChain;
+import android.security.KeyChain.KeyChainConnection;
 import android.security.KeyStore;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.widget.ILockSettings;
 import com.android.internal.widget.ILockSettingsObserver;
 import com.android.internal.widget.LockPatternUtils;
@@ -99,8 +106,30 @@
 
         mLockPatternUtils = new LockPatternUtils(context);
         mFirstCallToVold = true;
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_USER_ADDED);
+        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
     }
 
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // Update keystore settings for profiles which use the same password as their parent
+            if (Intent.ACTION_USER_STARTED.equals(intent.getAction())) {
+                final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+                final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
+                final UserInfo parentInfo = um.getProfileParent(userHandle);
+                if (parentInfo != null) {
+                    final KeyStore ks = KeyStore.getInstance();
+                    final int profileUid = UserHandle.getUid(userHandle, Process.SYSTEM_UID);
+                    final int parentUid = UserHandle.getUid(parentInfo.id, Process.SYSTEM_UID);
+                    ks.syncUid(parentUid, profileUid);
+                }
+            }
+        }
+    };
+
     public void systemReady() {
         migrateOldData();
     }
@@ -275,6 +304,17 @@
         }
     }
 
+    private int getUserParentOrSelfId(int userId) {
+        if (userId != 0) {
+            final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
+            final UserInfo pi = um.getProfileParent(userId);
+            if (pi != null) {
+                return pi.id;
+            }
+        }
+        return userId;
+    }
+
     private String getLockPatternFilename(int userId) {
         String dataSystemDirectory =
                 android.os.Environment.getDataDirectory().getAbsolutePath() +
@@ -283,6 +323,7 @@
             // Leave it in the same place for user 0
             return dataSystemDirectory + LOCK_PATTERN_FILE;
         } else {
+            userId = getUserParentOrSelfId(userId);
             return  new File(Environment.getUserSystemDirectory(userId), LOCK_PATTERN_FILE)
                     .getAbsolutePath();
         }
@@ -296,7 +337,8 @@
             // Leave it in the same place for user 0
             return dataSystemDirectory + LOCK_PASSWORD_FILE;
         } else {
-            return  new File(Environment.getUserSystemDirectory(userId), LOCK_PASSWORD_FILE)
+            userId = getUserParentOrSelfId(userId);
+            return new File(Environment.getUserSystemDirectory(userId), LOCK_PASSWORD_FILE)
                     .getAbsolutePath();
         }
     }
@@ -315,16 +357,27 @@
         return new File(getLockPatternFilename(userId)).length() > 0;
     }
 
-    private void maybeUpdateKeystore(String password, int userId) {
-        if (userId == UserHandle.USER_OWNER) {
-            final KeyStore keyStore = KeyStore.getInstance();
-            // Conditionally reset the keystore if empty. If non-empty, we are just
-            // switching key guard type
-            if (TextUtils.isEmpty(password) && keyStore.isEmpty()) {
-                keyStore.reset();
+    private void maybeUpdateKeystore(String password, int userHandle) {
+        final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
+        final KeyStore ks = KeyStore.getInstance();
+
+        final List<UserInfo> profiles = um.getProfiles(userHandle);
+        boolean shouldReset = TextUtils.isEmpty(password);
+
+        // For historical reasons, don't wipe a non-empty keystore if we have a single user with a
+        // single profile.
+        if (userHandle == UserHandle.USER_OWNER && profiles.size() == 1) {
+            if (!ks.isEmpty()) {
+                shouldReset = false;
+            }
+        }
+
+        for (UserInfo pi : profiles) {
+            final int profileUid = UserHandle.getUid(pi.id, Process.SYSTEM_UID);
+            if (shouldReset) {
+                ks.resetUid(profileUid);
             } else {
-                // Update the keystore password
-                keyStore.password(password);
+                ks.passwordUid(password, profileUid);
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1367761..c2c86ff 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8948,6 +8948,14 @@
         return false;
     }
 
+    private void checkTime(long startTime, String where) {
+        long now = SystemClock.elapsedRealtime();
+        if ((now-startTime) > 1000) {
+            // If we are taking more than a second, log about it.
+            Slog.w(TAG, "Slow operation: " + (now-startTime) + "ms so far, now at " + where);
+        }
+    }
+
     private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
             String name, IBinder token, boolean stable, int userId) {
         ContentProviderRecord cpr;
@@ -8955,6 +8963,8 @@
         ProviderInfo cpi = null;
 
         synchronized(this) {
+            long startTime = SystemClock.elapsedRealtime();
+
             ProcessRecord r = null;
             if (caller != null) {
                 r = getRecordForAppLocked(caller);
@@ -8968,6 +8978,8 @@
 
             boolean checkCrossUser = true;
 
+            checkTime(startTime, "getContentProviderImpl: getProviderByName");
+
             // First check if this content provider has been published...
             cpr = mProviderMap.getProviderByName(name, userId);
             // If that didn't work, check if it exists for user 0 and then
@@ -8992,10 +9004,12 @@
             if (providerRunning) {
                 cpi = cpr.info;
                 String msg;
+                checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");
                 if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
                         != null) {
                     throw new SecurityException(msg);
                 }
+                checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");
 
                 if (r != null && cpr.canRunHere(r)) {
                     // This provider has been published or is in the process
@@ -9011,6 +9025,8 @@
 
                 final long origId = Binder.clearCallingIdentity();
 
+                checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");
+
                 // In this case the provider instance already exists, so we can
                 // return it right away.
                 conn = incProviderCountLocked(r, cpr, token, stable);
@@ -9020,7 +9036,9 @@
                         // make sure to count it as being accessed and thus
                         // back up on the LRU list.  This is good because
                         // content providers are often expensive to start.
+                        checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
                         updateLruProcessLocked(cpr.proc, false, null);
+                        checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
                     }
                 }
 
@@ -9033,7 +9051,9 @@
                             Process.killProcess(cpr.proc.pid);
                         }
                     }
+                    checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
                     boolean success = updateOomAdjLocked(cpr.proc);
+                    checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
                     if (DEBUG_PROVIDER) Slog.i(TAG, "Adjust success: " + success);
                     // NOTE: there is still a race here where a signal could be
                     // pending on the process even though we managed to update its
@@ -9048,7 +9068,9 @@
                                 "Existing provider " + cpr.name.flattenToShortString()
                                 + " is crashing; detaching " + r);
                         boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
+                        checkTime(startTime, "getContentProviderImpl: before appDied");
                         appDiedLocked(cpr.proc);
+                        checkTime(startTime, "getContentProviderImpl: after appDied");
                         if (!lastRef) {
                             // This wasn't the last ref our process had on
                             // the provider...  we have now been killed, bail.
@@ -9065,9 +9087,11 @@
             boolean singleton;
             if (!providerRunning) {
                 try {
+                    checkTime(startTime, "getContentProviderImpl: before resolveContentProvider");
                     cpi = AppGlobals.getPackageManager().
                         resolveContentProvider(name,
                             STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
+                    checkTime(startTime, "getContentProviderImpl: after resolveContentProvider");
                 } catch (RemoteException ex) {
                 }
                 if (cpi == null) {
@@ -9084,12 +9108,15 @@
                     userId = UserHandle.USER_OWNER;
                 }
                 cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
+                checkTime(startTime, "getContentProviderImpl: got app info for user");
 
                 String msg;
+                checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");
                 if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
                         != null) {
                     throw new SecurityException(msg);
                 }
+                checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");
 
                 if (!mProcessesReady && !mDidUpdate && !mWaitingUpdate
                         && !cpi.processName.equals("system")) {
@@ -9111,15 +9138,19 @@
                 }
 
                 ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
+                checkTime(startTime, "getContentProviderImpl: before getProviderByClass");
                 cpr = mProviderMap.getProviderByClass(comp, userId);
+                checkTime(startTime, "getContentProviderImpl: after getProviderByClass");
                 final boolean firstClass = cpr == null;
                 if (firstClass) {
                     try {
+                        checkTime(startTime, "getContentProviderImpl: before getApplicationInfo");
                         ApplicationInfo ai =
                             AppGlobals.getPackageManager().
                                 getApplicationInfo(
                                         cpi.applicationInfo.packageName,
                                         STOCK_PM_FLAGS, userId);
+                        checkTime(startTime, "getContentProviderImpl: after getApplicationInfo");
                         if (ai == null) {
                             Slog.w(TAG, "No package info for content provider "
                                     + cpi.name);
@@ -9132,6 +9163,8 @@
                     }
                 }
 
+                checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord");
+
                 if (r != null && cpr.canRunHere(r)) {
                     // If this is a multiprocess provider, then just return its
                     // info and allow the caller to instantiate it.  Only do
@@ -9165,8 +9198,10 @@
                     try {
                         // Content provider is now in use, its package can't be stopped.
                         try {
+                            checkTime(startTime, "getContentProviderImpl: before set stopped state");
                             AppGlobals.getPackageManager().setPackageStoppedState(
                                     cpr.appInfo.packageName, false, userId);
+                            checkTime(startTime, "getContentProviderImpl: after set stopped state");
                         } catch (RemoteException e) {
                         } catch (IllegalArgumentException e) {
                             Slog.w(TAG, "Failed trying to unstop package "
@@ -9174,22 +9209,26 @@
                         }
 
                         // Use existing process if already started
+                        checkTime(startTime, "getContentProviderImpl: looking for process record");
                         ProcessRecord proc = getProcessRecordLocked(
                                 cpi.processName, cpr.appInfo.uid, false);
                         if (proc != null && proc.thread != null) {
                             if (DEBUG_PROVIDER) {
                                 Slog.d(TAG, "Installing in existing process " + proc);
                             }
+                            checkTime(startTime, "getContentProviderImpl: scheduling install");
                             proc.pubProviders.put(cpi.name, cpr);
                             try {
                                 proc.thread.scheduleInstallProvider(cpi);
                             } catch (RemoteException e) {
                             }
                         } else {
+                            checkTime(startTime, "getContentProviderImpl: before start process");
                             proc = startProcessLocked(cpi.processName,
                                     cpr.appInfo, false, 0, "content provider",
                                     new ComponentName(cpi.applicationInfo.packageName,
                                             cpi.name), false, false, false);
+                            checkTime(startTime, "getContentProviderImpl: after start process");
                             if (proc == null) {
                                 Slog.w(TAG, "Unable to launch app "
                                         + cpi.applicationInfo.packageName + "/"
@@ -9205,6 +9244,8 @@
                     }
                 }
 
+                checkTime(startTime, "getContentProviderImpl: updating data structures");
+
                 // Make sure the provider is published (the same provider class
                 // may be published under multiple names).
                 if (firstClass) {
@@ -9217,6 +9258,7 @@
                     conn.waiting = true;
                 }
             }
+            checkTime(startTime, "getContentProviderImpl: done!");
         }
 
         // Wait for the provider to be published...
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 1d2f7a9..6545134 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2072,7 +2072,7 @@
             }
             targetStack = inTask.stack;
             targetStack.moveTaskToFrontLocked(inTask, r, options);
-            mWindowManager.moveTaskToTop(targetStack.topTask().taskId);
+            mWindowManager.moveTaskToTop(inTask.taskId);
 
             // Check whether we should actually launch the new activity in to the task,
             // or just reuse the current activity on top.
diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java
index ff22764..a37249d 100644
--- a/services/core/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/core/java/com/android/server/am/ContentProviderRecord.java
@@ -166,8 +166,16 @@
         }
         if (full) {
             if (hasExternalProcessHandles()) {
-                pw.print(prefix); pw.print("externals=");
-                        pw.println(externalProcessTokenToHandle.size());
+                pw.print(prefix); pw.print("externals:");
+                if (externalProcessTokenToHandle != null) {
+                    pw.print(" w/token=");
+                    pw.print(externalProcessTokenToHandle.size());
+                }
+                if (externalProcessNoHandleCount > 0) {
+                    pw.print(" notoken=");
+                    pw.print(externalProcessNoHandleCount);
+                }
+                pw.println();
             }
         } else {
             if (connections.size() > 0 || externalProcessNoHandleCount > 0) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 77c324f..1287dce 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -21,6 +21,7 @@
 import java.nio.ByteBuffer;
 
 import android.app.ActivityManager;
+import android.os.SystemClock;
 import com.android.internal.util.MemInfoReader;
 import com.android.server.wm.WindowManagerService;
 
@@ -528,12 +529,18 @@
         if (amt == UNKNOWN_ADJ)
             return;
 
+        long start = SystemClock.elapsedRealtime();
         ByteBuffer buf = ByteBuffer.allocate(4 * 4);
         buf.putInt(LMK_PROCPRIO);
         buf.putInt(pid);
         buf.putInt(uid);
         buf.putInt(amt);
         writeLmkd(buf);
+        long now = SystemClock.elapsedRealtime();
+        if ((now-start) > 250) {
+            Slog.w("ActivityManager", "SLOW OOM ADJ: " + (now-start) + "ms for pid " + pid
+                    + " = " + amt);
+        }
     }
 
     /*
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 38077eb..8c342dd 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1009,7 +1009,7 @@
                 y[i] = normalizeAbsoluteBrightness(brightness[i]);
             }
 
-            Spline spline = Spline.createMonotoneCubicSpline(x, y);
+            Spline spline = Spline.createSpline(x, y);
             if (DEBUG) {
                 Slog.d(TAG, "Auto-brightness spline: " + spline);
                 for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 827b3ed..bb22b4d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -23,6 +23,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Predicate;
 import com.android.server.hdmi.HdmiAnnotations.IoThreadOnly;
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
@@ -30,6 +31,8 @@
 
 import libcore.util.EmptyArray;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -446,7 +449,7 @@
                         allocated.add(address);
                     }
                 }
-                mIoThreadLogger.debug("DevicePollingResult:" + allocated);
+                mIoThreadLogger.debug("[P]:Allocated Address=" + allocated);
                 if (callback != null) {
                     runOnServiceThread(new Runnable() {
                         @Override
@@ -548,7 +551,7 @@
         runOnIoThread(new Runnable() {
             @Override
             public void run() {
-                mIoThreadLogger.debug("SendCommand:" + cecMessage);
+                mIoThreadLogger.debug("[S]:" + cecMessage);
                 byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
                 int i = 0;
                 int errorCode = Constants.SEND_RESULT_SUCCESS;
@@ -583,7 +586,7 @@
     private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
         assertRunOnServiceThread();
         HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
-        mServiceThreadLogger.debug("ReceiveCommand:" + command);
+        mServiceThreadLogger.debug("[R]:" + command);
         onReceiveCommand(command);
     }
 
@@ -598,6 +601,15 @@
         mService.onHotplug(port, connected);
     }
 
+    void dump(final IndentingPrintWriter pw) {
+        for (int i = 0; i < mLocalDevices.size(); ++i) {
+            pw.println("HdmiCecLocalDevice #" + i + ":");
+            pw.increaseIndent();
+            mLocalDevices.valueAt(i).dump(pw);
+            pw.decreaseIndent();
+        }
+    }
+
     private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
     private static native int nativeSendCecCommand(long controllerPtr, int srcAddress,
             int dstAddress, byte[] body);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
index 26d2cde..85f5be2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
@@ -43,6 +43,9 @@
  */
 abstract class HdmiCecFeatureAction {
     private static final String TAG = "HdmiCecFeatureAction";
+    // As all actions run in the same thread (service thread), it's fine to have single logger.
+    // TODO: create global logger for each threads and use them.
+    protected static final HdmiLogger DLOGGER = new HdmiLogger(TAG);
 
     // Timer handler message used for timeout event
     protected static final int MSG_TIMEOUT = 100;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index a12e4fc..38addba 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -28,6 +28,7 @@
 import android.view.KeyEvent;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 
 import java.util.ArrayList;
@@ -97,6 +98,17 @@
         public int hashCode() {
             return logicalAddress * 29 + physicalAddress;
         }
+        @Override
+        public String toString() {
+            StringBuffer s = new StringBuffer();
+            String logicalAddressString = (logicalAddress == Constants.ADDR_INVALID)
+                    ? "invalid" : String.format("0x%02x", logicalAddress);
+            s.append("logical_address: ").append(logicalAddressString);
+            String physicalAddressString = (physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS)
+                    ? "invalid" : String.format("0x%04x", physicalAddress);
+            s.append(", physical_address: ").append(physicalAddressString);
+            return s.toString();
+        }
     }
     // Logical address of the active source.
     @GuardedBy("mLock")
@@ -793,4 +805,16 @@
     protected void sendKeyEvent(int keyCode, boolean isPressed) {
         Slog.w(TAG, "sendKeyEvent not implemented");
     }
+
+    /**
+     * Dump internal status of HdmiCecLocalDevice object.
+     */
+    protected void dump(final IndentingPrintWriter pw) {
+        pw.println("mDeviceType: " + mDeviceType);
+        pw.println("mAddress: " + mAddress);
+        pw.println("mPreferredAddress: " + mPreferredAddress);
+        pw.println("mDeviceInfo: " + mDeviceInfo);
+        pw.println("mActiveSource: " + mActiveSource);
+        pw.println(String.format("mActiveRoutingPath: 0x%04x", mActiveRoutingPath));
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 5a2fa9c..6603a71 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -23,6 +23,7 @@
 import android.os.SystemProperties;
 import android.util.Slog;
 
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 
 /**
@@ -219,4 +220,10 @@
         mIsActiveSource = false;
         checkIfPendingActionsCleared();
     }
+
+    @Override
+    protected void dump(final IndentingPrintWriter pw) {
+        super.dump(pw);
+        pw.println("mIsActiveSource: " + mIsActiveSource);
+    }
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index cd56cfc..1ab8069 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -45,6 +45,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
@@ -1610,4 +1611,16 @@
 
         invokeDeviceEventListener(newInfo, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
     }
+
+    @Override
+    protected void dump(final IndentingPrintWriter pw) {
+        super.dump(pw);
+        pw.println("mArcEstablished: " + mArcEstablished);
+        pw.println("mArcFeatureEnabled: " + mArcFeatureEnabled);
+        pw.println("mSystemAudioActivated: " + mSystemAudioActivated);
+        pw.println("mSystemAudioMute: " + mSystemAudioMute);
+        pw.println("mAutoDeviceOff: " + mAutoDeviceOff);
+        pw.println("mAutoWakeup: " + mAutoWakeup);
+        pw.println("mSkipRoutingControl: " + mSkipRoutingControl);
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index e7b920a..d13e1de 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -67,6 +67,7 @@
 import android.util.SparseIntArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.SystemService;
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
@@ -75,6 +76,8 @@
 
 import libcore.util.EmptyArray;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -1395,6 +1398,28 @@
             enforceAccessPermission();
             HdmiControlService.this.addHdmiMhlScratchpadCommandListener(listener);
         }
+
+        @Override
+        protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
+            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
+            final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
+
+            pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
+            pw.println("mProhibitMode: " + mProhibitMode);
+            if (mCecController != null) {
+                pw.println("mCecController: ");
+                pw.increaseIndent();
+                mCecController.dump(pw);
+                pw.decreaseIndent();
+            }
+            pw.println("mPortInfo: ");
+            pw.increaseIndent();
+            for (HdmiPortInfo hdmiPortInfo : mPortInfo) {
+                pw.println("- " + hdmiPortInfo);
+            }
+            pw.decreaseIndent();
+            pw.println("mPowerStatus: " + mPowerStatus);
+        }
     }
 
     @ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/HdmiLogger.java b/services/core/java/com/android/server/hdmi/HdmiLogger.java
index ee9379d..c7add75 100644
--- a/services/core/java/com/android/server/hdmi/HdmiLogger.java
+++ b/services/core/java/com/android/server/hdmi/HdmiLogger.java
@@ -42,7 +42,7 @@
     private final String mTag;
 
     HdmiLogger(String tag) {
-        mTag = tag;
+        mTag = "HDMI:" + tag;
     }
 
     void warning(String logMessage) {
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index ac2c7b9..d15ffb0 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -73,9 +73,9 @@
 
     // Seq #27
     protected void sendSystemAudioModeRequest() {
-        mState = STATE_CHECK_ROUTING_IN_PRGRESS;
         List<RoutingControlAction> routingActions = getActions(RoutingControlAction.class);
         if (!routingActions.isEmpty()) {
+            mState = STATE_CHECK_ROUTING_IN_PRGRESS;
             // Should have only one Routing Control Action
             RoutingControlAction routingAction = routingActions.get(0);
             routingAction.addOnFinishedCallback(this, new Runnable() {
@@ -97,20 +97,21 @@
         sendCommand(command, new HdmiControlService.SendMessageCallback() {
             @Override
             public void onSendCompleted(int error) {
-                if (error == Constants.SEND_RESULT_SUCCESS) {
-                    mState = STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE;
-                    addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS);
-                } else {
+                if (error != Constants.SEND_RESULT_SUCCESS) {
+                    DLOGGER.debug("Failed to send <System Audio Mode Request>:" + error);
                     setSystemAudioMode(false);
                     finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
                 }
             }
         });
+        mState = STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE;
+        addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS);
     }
 
     private void handleSendSystemAudioModeRequestTimeout() {
         if (!mTargetAudioStatus  // Don't retry for Off case.
                 || mSendRetryCount++ >= MAX_SEND_RETRY_COUNT) {
+            DLOGGER.debug("[T]:wait for <Set System Audio Mode>.");
             setSystemAudioMode(false);
             finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
             return;
@@ -129,6 +130,7 @@
                 if (cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT
                         && (cmd.getParams()[0] & 0xFF)
                                 == Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST) {
+                    DLOGGER.debug("Failed to start system audio mode request.");
                     setSystemAudioMode(false);
                     finishWithCallback(HdmiControlManager.RESULT_EXCEPTION);
                     return true;
@@ -143,6 +145,7 @@
                     startAudioStatusAction();
                     return true;
                 } else {
+                    DLOGGER.debug("Unexpected system audio mode request:" + receivedStatus);
                     // Unexpected response, consider the request is newly initiated by AVR.
                     // To return 'false' will initiate new SystemAudioActionFromAvr by the control
                     // service.
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 379ec94..c3bc306 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -819,6 +819,7 @@
     };
 
     void dumpInternal(PrintWriter pw) {
+        final long now = SystemClock.elapsedRealtime();
         synchronized (mJobs) {
             pw.print("Started users: ");
             for (int i=0; i<mStartedUsers.size(); i++) {
@@ -833,15 +834,14 @@
                     job.dump(pw, "  ");
                 }
             } else {
-                pw.println();
-                pw.println("No jobs scheduled.");
+                pw.println("  None.");
             }
             for (int i=0; i<mControllers.size(); i++) {
                 pw.println();
                 mControllers.get(i).dumpControllerState(pw);
             }
             pw.println();
-            pw.println("Pending");
+            pw.println("Pending:");
             for (int i=0; i<mPendingJobs.size(); i++) {
                 pw.println(mPendingJobs.get(i).hashCode());
             }
@@ -852,10 +852,14 @@
                 if (jsc.isAvailable()) {
                     continue;
                 } else {
-                    pw.println(jsc.getRunningJob().hashCode() + " for: " +
-                            (SystemClock.elapsedRealtime()
-                                    - jsc.getExecutionStartTimeElapsed())/1000 + "s " +
-                            "timeout: " + jsc.getTimeoutElapsed());
+                    final long timeout = jsc.getTimeoutElapsed();
+                    pw.print("Running for: ");
+                    pw.print((now - jsc.getExecutionStartTimeElapsed())/1000);
+                    pw.print("s timeout=");
+                    pw.print(timeout);
+                    pw.print(" fromnow=");
+                    pw.println(timeout-now);
+                    jsc.getRunningJob().dump(pw, "  ");
                 }
             }
             pw.println();
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 6f5d3c2..f562721 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -251,6 +251,7 @@
 
     // Dumpsys infrastructure
     public void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix);
         pw.println(this.toString());
     }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index fc1b746..d0f4054 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -17,6 +17,8 @@
 package com.android.server.notification;
 
 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
+import static android.service.notification.NotificationListenerService.TRIM_FULL;
+import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.END_TAG;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -50,6 +52,7 @@
 import android.media.IRingtonePlayer;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -1290,24 +1293,23 @@
          */
         @Override
         public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
-                INotificationListener token, String[] keys) {
+                INotificationListener token, String[] keys, int trim) {
             synchronized (mNotificationList) {
                 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
-                final ArrayList<StatusBarNotification> list
-                        = new ArrayList<StatusBarNotification>();
                 final boolean getKeys = keys != null;
                 final int N = getKeys ? keys.length : mNotificationList.size();
-                list.ensureCapacity(N);
+                final ArrayList<StatusBarNotification> list
+                        = new ArrayList<StatusBarNotification>(N);
                 for (int i=0; i<N; i++) {
                     final NotificationRecord r = getKeys
                             ? mNotificationsByKey.get(keys[i])
                             : mNotificationList.get(i);
-                    if (r != null) {
-                        StatusBarNotification sbn = r.sbn;
-                        if (isVisibleToListener(sbn, info)) {
-                            list.add(sbn);
-                        }
-                    }
+                    if (r == null) continue;
+                    StatusBarNotification sbn = r.sbn;
+                    if (!isVisibleToListener(sbn, info)) continue;
+                    StatusBarNotification sbnToSend =
+                            (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
+                    list.add(sbnToSend);
                 }
                 return new ParceledListSlice<StatusBarNotification>(list);
             }
@@ -1364,6 +1366,16 @@
         }
 
         @Override
+        public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
+                throws RemoteException {
+            synchronized (mNotificationList) {
+                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+                if (info == null) return;
+                mListeners.setOnNotificationPostedTrimLocked(info, trim);
+            }
+        }
+
+        @Override
         public ZenModeConfig getZenModeConfig() {
             enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
             return mZenModeHelper.getConfig();
@@ -1427,7 +1439,7 @@
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                     != PackageManager.PERMISSION_GRANTED) {
-                pw.println("Permission Denial: can't dump NotificationManager from from pid="
+                pw.println("Permission Denial: can't dump NotificationManager from pid="
                         + Binder.getCallingPid()
                         + ", uid=" + Binder.getCallingUid());
                 return;
@@ -1441,6 +1453,13 @@
             enforceSystemOrSystemUI("INotificationManager.getEffectsSuppressor");
             return mEffectsSuppressor;
         }
+
+        @Override
+        public boolean matchesCallFilter(Bundle extras) {
+            enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
+            return mZenModeHelper.matchesCallFilter(extras,
+                    mRankingHelper.findExtractor(ValidateNotificationPeople.class));
+        }
     };
 
     private String[] getActiveNotificationKeys(INotificationListener token) {
@@ -2611,6 +2630,8 @@
 
     public class NotificationListeners extends ManagedServices {
 
+        private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
+
         public NotificationListeners() {
             super(getContext(), mHandler, mNotificationList, mUserProfiles);
         }
@@ -2651,6 +2672,20 @@
             if (mListenersDisablingEffects.remove(removed)) {
                 updateListenerHintsLocked();
             }
+            mLightTrimListeners.remove(removed);
+        }
+
+        public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
+            if (trim == TRIM_LIGHT) {
+                mLightTrimListeners.add(info);
+            } else {
+                mLightTrimListeners.remove(info);
+            }
+        }
+
+        public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
+            return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
+
         }
 
         /**
@@ -2661,8 +2696,10 @@
          * but isn't anymore.
          */
         public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
-            // make a copy in case changes are made to the underlying Notification object
-            final StatusBarNotification sbnClone = sbn.clone();
+            // Lazily initialized snapshots of the notification.
+            StatusBarNotification sbnClone = null;
+            StatusBarNotification sbnCloneLight = null;
+
             for (final ManagedServiceInfo info : mServices) {
                 boolean sbnVisible = isVisibleToListener(sbn, info);
                 boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
@@ -2684,10 +2721,20 @@
                     continue;
                 }
 
+                final int trim = mListeners.getOnNotificationPostedTrim(info);
+
+                if (trim == TRIM_LIGHT && sbnCloneLight == null) {
+                    sbnCloneLight = sbn.cloneLight();
+                } else if (trim == TRIM_FULL && sbnClone == null) {
+                    sbnClone = sbn.clone();
+                }
+                final StatusBarNotification sbnToPost =
+                        (trim == TRIM_FULL) ? sbnClone : sbnCloneLight;
+
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
-                        notifyPosted(info, sbnClone, update);
+                        notifyPosted(info, sbnToPost, update);
                     }
                 });
             }
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 01188af..435177b 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -87,6 +87,17 @@
         mProxyByGroupTmp = new ArrayMap<String, NotificationRecord>();
     }
 
+    public <T extends NotificationSignalExtractor> T findExtractor(Class<T> extractorClass) {
+        final int N = mSignalExtractors.length;
+        for (int i = 0; i < N; i++) {
+            final NotificationSignalExtractor extractor = mSignalExtractors[i];
+            if (extractorClass.equals(extractor.getClass())) {
+                return (T) extractor;
+            }
+        }
+        return null;
+    }
+
     public void extractSignals(NotificationRecord r) {
         final int N = mSignalExtractors.length;
         for (int i = 0; i < N; i++) {
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index bdc364c..aa47858 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -71,8 +71,17 @@
     private LruCache<String, LookupResult> mPeopleCache;
 
     private RankingReconsideration validatePeople(final NotificationRecord record) {
+        final String key = record.getKey();
+        final Bundle extras = record.getNotification().extras;
+        final float[] affinityOut = new float[1];
+        final RankingReconsideration rr = validatePeople(key, extras, affinityOut);
+        record.setContactAffinity(affinityOut[0]);
+        return rr;
+    }
+
+    private PeopleRankingReconsideration validatePeople(String key, Bundle extras,
+            float[] affinityOut) {
         float affinity = NONE;
-        Bundle extras = record.getNotification().extras;
         if (extras == null) {
             return null;
         }
@@ -82,7 +91,7 @@
             return null;
         }
 
-        if (INFO) Slog.i(TAG, "Validating: " + record.sbn.getKey());
+        if (INFO) Slog.i(TAG, "Validating: " + key);
         final LinkedList<String> pendingLookups = new LinkedList<String>();
         for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) {
             final String handle = people[personIdx];
@@ -102,51 +111,15 @@
         }
 
         // record the best available data, so far:
-        record.setContactAffinity(affinity);
+        affinityOut[0] = affinity;
 
         if (pendingLookups.isEmpty()) {
             if (INFO) Slog.i(TAG, "final affinity: " + affinity);
             return null;
         }
 
-        if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + record.sbn.getKey());
-        return new RankingReconsideration(record.getKey()) {
-            float mContactAffinity = NONE;
-            @Override
-            public void work() {
-                if (INFO) Slog.i(TAG, "Executing: validation for: " + record.getKey());
-                for (final String handle: pendingLookups) {
-                    LookupResult lookupResult = null;
-                    final Uri uri = Uri.parse(handle);
-                    if ("tel".equals(uri.getScheme())) {
-                        if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
-                        lookupResult = resolvePhoneContact(uri.getSchemeSpecificPart());
-                    } else if ("mailto".equals(uri.getScheme())) {
-                        if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
-                        lookupResult = resolveEmailContact(uri.getSchemeSpecificPart());
-                    } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
-                        if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
-                        lookupResult = searchContacts(uri);
-                    } else {
-                        lookupResult = new LookupResult();  // invalid person for the cache
-                        Slog.w(TAG, "unsupported URI " + handle);
-                    }
-                    if (lookupResult != null) {
-                        synchronized (mPeopleCache) {
-                            mPeopleCache.put(handle, lookupResult);
-                        }
-                        mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
-                    }
-                }
-            }
-
-            @Override
-            public void applyChangesLocked(NotificationRecord operand) {
-                float affinityBound = operand.getContactAffinity();
-                operand.setContactAffinity(Math.max(mContactAffinity, affinityBound));
-                if (INFO) Slog.i(TAG, "final affinity: " + operand.getContactAffinity());
-            }
-        };
+        if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + key);
+        return new PeopleRankingReconsideration(key, pendingLookups);
     }
 
     // VisibleForTesting
@@ -269,6 +242,19 @@
         // ignore: config has no relevant information yet.
     }
 
+    public float getContactAffinity(Bundle extras) {
+        if (extras == null) return NONE;
+        final String key = Long.toString(System.nanoTime());
+        final float[] affinityOut = new float[1];
+        final PeopleRankingReconsideration prr = validatePeople(key, extras, affinityOut);
+        float affinity = affinityOut[0];
+        if (prr != null) {
+            prr.work();
+            affinity = Math.max(prr.getContactAffinity(), affinity);
+        }
+        return affinity;
+    }
+
     private static class LookupResult {
         private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000;  // 1hr
         public static final int INVALID_ID = -1;
@@ -328,5 +314,55 @@
             return this;
         }
     }
+
+    private class PeopleRankingReconsideration extends RankingReconsideration {
+        private final LinkedList<String> mPendingLookups;
+
+        private float mContactAffinity = NONE;
+
+        private PeopleRankingReconsideration(String key, LinkedList<String> pendingLookups) {
+            super(key);
+            mPendingLookups = pendingLookups;
+        }
+
+        @Override
+        public void work() {
+            if (INFO) Slog.i(TAG, "Executing: validation for: " + mKey);
+            for (final String handle: mPendingLookups) {
+                LookupResult lookupResult = null;
+                final Uri uri = Uri.parse(handle);
+                if ("tel".equals(uri.getScheme())) {
+                    if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
+                    lookupResult = resolvePhoneContact(uri.getSchemeSpecificPart());
+                } else if ("mailto".equals(uri.getScheme())) {
+                    if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
+                    lookupResult = resolveEmailContact(uri.getSchemeSpecificPart());
+                } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
+                    if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
+                    lookupResult = searchContacts(uri);
+                } else {
+                    lookupResult = new LookupResult();  // invalid person for the cache
+                    Slog.w(TAG, "unsupported URI " + handle);
+                }
+                if (lookupResult != null) {
+                    synchronized (mPeopleCache) {
+                        mPeopleCache.put(handle, lookupResult);
+                    }
+                    mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
+                }
+            }
+        }
+
+        @Override
+        public void applyChangesLocked(NotificationRecord operand) {
+            float affinityBound = operand.getContactAffinity();
+            operand.setContactAffinity(Math.max(mContactAffinity, affinityBound));
+            if (INFO) Slog.i(TAG, "final affinity: " + operand.getContactAffinity());
+        }
+
+        public float getContactAffinity() {
+            return mContactAffinity;
+        }
+    }
 }
 
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 7a5336b..fd35ede 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -34,6 +34,7 @@
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.UserHandle;
 import android.provider.Settings.Global;
@@ -189,7 +190,7 @@
     }
 
     private boolean shouldInterceptAudience(NotificationRecord record) {
-        if (!audienceMatches(record)) {
+        if (!audienceMatches(record.getContactAffinity())) {
             ZenLog.traceIntercepted(record, "!audienceMatches");
             return true;
         }
@@ -372,14 +373,27 @@
         return record.isCategory(Notification.CATEGORY_MESSAGE) || isDefaultMessagingApp(record);
     }
 
-    private boolean audienceMatches(NotificationRecord record) {
+    public boolean matchesCallFilter(Bundle extras, ValidateNotificationPeople validator) {
+        final int zen = mZenMode;
+        if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) return false; // nothing gets through
+        if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
+            if (!mConfig.allowCalls) return false; // no calls get through
+            if (validator != null) {
+                final float contactAffinity = validator.getContactAffinity(extras);
+                return audienceMatches(contactAffinity);
+            }
+        }
+        return true;
+    }
+
+    private boolean audienceMatches(float contactAffinity) {
         switch (mConfig.allowFrom) {
             case ZenModeConfig.SOURCE_ANYONE:
                 return true;
             case ZenModeConfig.SOURCE_CONTACT:
-                return record.getContactAffinity() >= ValidateNotificationPeople.VALID_CONTACT;
+                return contactAffinity >= ValidateNotificationPeople.VALID_CONTACT;
             case ZenModeConfig.SOURCE_STAR:
-                return record.getContactAffinity() >= ValidateNotificationPeople.STARRED_CONTACT;
+                return contactAffinity >= ValidateNotificationPeople.STARRED_CONTACT;
             default:
                 Slog.w(TAG, "Encountered unknown source: " + mConfig.allowFrom);
                 return true;
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 694669c..ca11862 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -224,10 +224,6 @@
         }
     }
 
-    public int pruneDexCache(String cacheSubDir) {
-        return mInstaller.execute("prunedexcache " + cacheSubDir);
-    }
-
     public int freeCache(long freeStorageSize) {
         StringBuilder builder = new StringBuilder("freecache");
         builder.append(' ');
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 89878ef..5aa0294 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -104,6 +104,7 @@
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
+import android.content.pm.KeySet;
 import android.content.pm.ManifestDigest;
 import android.content.pm.PackageCleanItem;
 import android.content.pm.PackageInfo;
@@ -1497,29 +1498,6 @@
                 }
             }
 
-            if (didDexOptLibraryOrTool) {
-                // If we dexopted a library or tool, then something on the system has
-                // changed. Consider this significant, and wipe away all other
-                // existing dexopt files to ensure we don't leave any dangling around.
-                //
-                // TODO: This should be revisited because it isn't as good an indicator
-                // as it used to be. It used to include the boot classpath but at some point
-                // DexFile.isDexOptNeeded started returning false for the boot
-                // class path files in all cases. It is very possible in a
-                // small maintenance release update that the library and tool
-                // jars may be unchanged but APK could be removed resulting in
-                // unused dalvik-cache files.
-                for (String dexCodeInstructionSet : dexCodeInstructionSets) {
-                    mInstaller.pruneDexCache(dexCodeInstructionSet);
-                }
-
-                // Additionally, delete all dex files from the root directory
-                // since there shouldn't be any there anyway, unless we're upgrading
-                // from an older OS version or a build that contained the "old" style
-                // flat scheme.
-                mInstaller.pruneDexCache(".");
-            }
-
             // Collect vendor overlay packages.
             // (Do this before scanning any apps.)
             // For security and version matching reason, only consider
@@ -7721,8 +7699,21 @@
     public void installPackage(String originPath, IPackageInstallObserver2 observer,
             int installFlags, String installerPackageName, VerificationParams verificationParams,
             String packageAbiOverride) {
+        installPackageAsUser(originPath, observer, installFlags, installerPackageName, verificationParams,
+                packageAbiOverride, UserHandle.getCallingUserId());
+    }
+
+    @Override
+    public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
+            int installFlags, String installerPackageName, VerificationParams verificationParams,
+            String packageAbiOverride, int userId) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
                 null);
+        if (UserHandle.getCallingUserId() != userId) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                    "installPackage " + userId);
+        }
 
         final File originFile = new File(originPath);
         final int uid = Binder.getCallingUid();
@@ -7740,7 +7731,7 @@
         if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
             user = UserHandle.ALL;
         } else {
-            user = new UserHandle(UserHandle.getUserId(uid));
+            user = new UserHandle(userId);
         }
 
         final int filteredInstallFlags;
@@ -12988,7 +12979,7 @@
     }
 
     /** Called by UserManagerService */
-    void cleanUpUserLILPw(int userHandle) {
+    void cleanUpUserLILPw(UserManagerService userManager, int userHandle) {
         mDirtyUsers.remove(userHandle);
         mSettings.removeUserLPw(userHandle);
         mPendingBroadcasts.remove(userHandle);
@@ -12999,6 +12990,50 @@
             mInstaller.removeUserDataDirs(userHandle);
         }
         mUserNeedsBadging.delete(userHandle);
+        removeUnusedPackagesLILPw(userManager, userHandle);
+    }
+
+    /**
+     * We're removing userHandle and would like to remove any downloaded packages
+     * that are no longer in use by any other user.
+     * @param userHandle the user being removed
+     */
+    private void removeUnusedPackagesLILPw(UserManagerService userManager, final int userHandle) {
+        final boolean DEBUG_CLEAN_APKS = false;
+        int [] users = userManager.getUserIdsLPr();
+        Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
+        while (psit.hasNext()) {
+            PackageSetting ps = psit.next();
+            final String packageName = ps.pkg.packageName;
+            // Skip over if system app
+            if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                continue;
+            }
+            if (DEBUG_CLEAN_APKS) {
+                Slog.i(TAG, "Checking package " + packageName);
+            }
+            boolean keep = false;
+            for (int i = 0; i < users.length; i++) {
+                if (users[i] != userHandle && ps.getInstalled(users[i])) {
+                    keep = true;
+                    if (DEBUG_CLEAN_APKS) {
+                        Slog.i(TAG, "  Keeping package " + packageName + " for user "
+                                + users[i]);
+                    }
+                    break;
+                }
+            }
+            if (!keep) {
+                if (DEBUG_CLEAN_APKS) {
+                    Slog.i(TAG, "  Removing package " + packageName);
+                }
+                mHandler.post(new Runnable() {
+                    public void run() {
+                        deletePackageX(packageName, userHandle, 0);
+                    } //end run
+                });
+            }
+        }
     }
 
     /** Called by UserManagerService */
@@ -13098,7 +13133,7 @@
     }
 
     @Override
-    public KeySetHandle getKeySetByAlias(String packageName, String alias) {
+    public KeySet getKeySetByAlias(String packageName, String alias) {
         if (packageName == null || alias == null) {
             return null;
         }
@@ -13108,18 +13143,13 @@
                 Slog.w(TAG, "KeySet requested for unknown package:" + packageName);
                 throw new IllegalArgumentException("Unknown package: " + packageName);
             }
-            if (pkg.applicationInfo.uid != Binder.getCallingUid()
-                    && Process.SYSTEM_UID != Binder.getCallingUid()) {
-                throw new SecurityException("May not access KeySets defined by"
-                        + " aliases in other applications.");
-            }
             KeySetManagerService ksms = mSettings.mKeySetManagerService;
-            return ksms.getKeySetByAliasAndPackageNameLPr(packageName, alias);
+            return new KeySet(ksms.getKeySetByAliasAndPackageNameLPr(packageName, alias));
         }
     }
 
     @Override
-    public KeySetHandle getSigningKeySet(String packageName) {
+    public KeySet getSigningKeySet(String packageName) {
         if (packageName == null) {
             return null;
         }
@@ -13134,12 +13164,12 @@
                 throw new SecurityException("May not access signing KeySet of other apps.");
             }
             KeySetManagerService ksms = mSettings.mKeySetManagerService;
-            return ksms.getSigningKeySetByPackageNameLPr(packageName);
+            return new KeySet(ksms.getSigningKeySetByPackageNameLPr(packageName));
         }
     }
 
     @Override
-    public boolean isPackageSignedByKeySet(String packageName, IBinder ks) {
+    public boolean isPackageSignedByKeySet(String packageName, KeySet ks) {
         if (packageName == null || ks == null) {
             return false;
         }
@@ -13149,16 +13179,17 @@
                 Slog.w(TAG, "KeySet requested for unknown package:" + packageName);
                 throw new IllegalArgumentException("Unknown package: " + packageName);
             }
-            if (ks instanceof KeySetHandle) {
+            IBinder ksh = ks.getToken();
+            if (ksh instanceof KeySetHandle) {
                 KeySetManagerService ksms = mSettings.mKeySetManagerService;
-                return ksms.packageIsSignedByLPr(packageName, (KeySetHandle) ks);
+                return ksms.packageIsSignedByLPr(packageName, (KeySetHandle) ksh);
             }
             return false;
         }
     }
 
     @Override
-    public boolean isPackageSignedByKeySetExactly(String packageName, IBinder ks) {
+    public boolean isPackageSignedByKeySetExactly(String packageName, KeySet ks) {
         if (packageName == null || ks == null) {
             return false;
         }
@@ -13168,9 +13199,10 @@
                 Slog.w(TAG, "KeySet requested for unknown package:" + packageName);
                 throw new IllegalArgumentException("Unknown package: " + packageName);
             }
-            if (ks instanceof KeySetHandle) {
+            IBinder ksh = ks.getToken();
+            if (ksh instanceof KeySetHandle) {
                 KeySetManagerService ksms = mSettings.mKeySetManagerService;
-                return ksms.packageIsSignedByExactlyLPr(packageName, (KeySetHandle) ks);
+                return ksms.packageIsSignedByExactlyLPr(packageName, (KeySetHandle) ksh);
             }
             return false;
         }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 8ded7ca..2929939 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -293,6 +293,10 @@
     private List<UserInfo> getProfilesLocked(int userId, boolean enabledOnly) {
         UserInfo user = getUserInfoLocked(userId);
         ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+        if (user == null) {
+            // Probably a dying user
+            return users;
+        }
         for (int i = 0; i < mUsers.size(); i++) {
             UserInfo profile = mUsers.valueAt(i);
             if (!isProfileOf(user, profile)) {
@@ -1280,7 +1284,7 @@
 
     private void removeUserStateLocked(final int userHandle) {
         // Cleanup package manager settings
-        mPm.cleanUpUserLILPw(userHandle);
+        mPm.cleanUpUserLILPw(this, userHandle);
 
         // Remove this user from the list
         mUsers.remove(userHandle);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index a49d8ab..d22912c 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -119,7 +119,7 @@
 
         mContext = context;
         mContentResolver = context.getContentResolver();
-        mWatchLogHandler = new WatchLogHandler(IoThread.get().getLooper());
+        mWatchLogHandler = new WatchLogHandler(mContentResolver, IoThread.get().getLooper());
 
         mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());
 
@@ -248,7 +248,7 @@
                     serviceState = new ServiceState(component, userId);
                     userState.serviceStateMap.put(component, serviceState);
                 } else {
-                    inputList.addAll(serviceState.mInputList);
+                    inputList.addAll(serviceState.inputList);
                 }
             } else {
                 try {
@@ -258,9 +258,6 @@
                     continue;
                 }
             }
-
-            // Reconnect the service if existing input is updated.
-            updateServiceConnectionLocked(component, userId);
             userState.packageSet.add(si.packageName);
         }
 
@@ -273,7 +270,7 @@
             if (state == null) {
                 state = new TvInputState();
             }
-            state.mInfo = info;
+            state.info = info;
             inputMap.put(info.getId(), state);
         }
 
@@ -285,7 +282,7 @@
 
         for (String inputId : userState.inputMap.keySet()) {
             if (!inputMap.containsKey(inputId)) {
-                TvInputInfo info = userState.inputMap.get(inputId).mInfo;
+                TvInputInfo info = userState.inputMap.get(inputId).info;
                 ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
                 if (serviceState != null) {
                     abortPendingCreateSessionRequestsLocked(serviceState, inputId, userId);
@@ -352,9 +349,9 @@
             }
             // Release created sessions.
             for (SessionState state : userState.sessionStateMap.values()) {
-                if (state.mSession != null) {
+                if (state.session != null) {
                     try {
-                        state.mSession.release();
+                        state.session.release();
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in release", e);
                     }
@@ -364,14 +361,14 @@
 
             // Unregister all callbacks and unbind all services.
             for (ServiceState serviceState : userState.serviceStateMap.values()) {
-                if (serviceState.mCallback != null) {
+                if (serviceState.callback != null) {
                     try {
-                        serviceState.mService.unregisterCallback(serviceState.mCallback);
+                        serviceState.service.unregisterCallback(serviceState.callback);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in unregisterCallback", e);
                     }
                 }
-                mContext.unbindService(serviceState.mConnection);
+                mContext.unbindService(serviceState.connection);
             }
             userState.serviceStateMap.clear();
 
@@ -412,7 +409,7 @@
             throw new IllegalArgumentException("Session state not found for token " + sessionToken);
         }
         // Only the application that requested this session or the system can access it.
-        if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.mCallingUid) {
+        if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.callingUid) {
             throw new SecurityException("Illegal access to the session with token " + sessionToken
                     + " from uid " + callingUid);
         }
@@ -424,10 +421,10 @@
     }
 
     private ITvInputSession getSessionLocked(SessionState sessionState) {
-        ITvInputSession session = sessionState.mSession;
+        ITvInputSession session = sessionState.session;
         if (session == null) {
             throw new IllegalStateException("Session not yet created for token "
-                    + sessionState.mSessionToken);
+                    + sessionState.sessionToken);
         }
         return session;
     }
@@ -439,8 +436,8 @@
     }
 
     private static boolean shouldMaintainConnection(ServiceState serviceState) {
-        return !serviceState.mSessionTokens.isEmpty() || serviceState.mIsHardware;
-        // TODO: Find a way to maintain connection only when necessary.
+        return !serviceState.sessionTokens.isEmpty() || serviceState.isHardware;
+        // TODO: Find a way to maintain connection to hardware TV input service only when necessary.
     }
 
     private void updateServiceConnectionLocked(ComponentName component, int userId) {
@@ -449,18 +446,18 @@
         if (serviceState == null) {
             return;
         }
-        if (serviceState.mReconnecting) {
-            if (!serviceState.mSessionTokens.isEmpty()) {
+        if (serviceState.reconnecting) {
+            if (!serviceState.sessionTokens.isEmpty()) {
                 // wait until all the sessions are removed.
                 return;
             }
-            serviceState.mReconnecting = false;
+            serviceState.reconnecting = false;
         }
         boolean maintainConnection = shouldMaintainConnection(serviceState);
-        if (serviceState.mService == null && maintainConnection && userId == mCurrentUserId) {
+        if (serviceState.service == null && maintainConnection && userId == mCurrentUserId) {
             // This means that the service is not yet connected but its state indicates that we
             // have pending requests. Then, connect the service.
-            if (serviceState.mBound) {
+            if (serviceState.bound) {
                 // We have already bound to the service so we don't try to bind again until after we
                 // unbind later on.
                 return;
@@ -470,18 +467,15 @@
             }
 
             Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component);
-            // Binding service may fail if the service is updating.
-            // In that case, the connection will be revived in buildTvInputListLocked called by
-            // onSomePackagesChanged.
-            serviceState.mBound = mContext.bindServiceAsUser(
-                    i, serviceState.mConnection, Context.BIND_AUTO_CREATE, new UserHandle(userId));
-        } else if (serviceState.mService != null && !maintainConnection) {
+            serviceState.bound = mContext.bindServiceAsUser(
+                    i, serviceState.connection, Context.BIND_AUTO_CREATE, new UserHandle(userId));
+        } else if (serviceState.service != null && !maintainConnection) {
             // This means that the service is already connected but its state indicates that we have
             // nothing to do with it. Then, disconnect the service.
             if (DEBUG) {
                 Slog.d(TAG, "unbindService(service=" + component + ")");
             }
-            mContext.unbindService(serviceState.mConnection);
+            mContext.unbindService(serviceState.connection);
             userState.serviceStateMap.remove(component);
         }
     }
@@ -491,19 +485,19 @@
         // Let clients know the create session requests are failed.
         UserState userState = getUserStateLocked(userId);
         List<SessionState> sessionsToAbort = new ArrayList<>();
-        for (IBinder sessionToken : serviceState.mSessionTokens) {
+        for (IBinder sessionToken : serviceState.sessionTokens) {
             SessionState sessionState = userState.sessionStateMap.get(sessionToken);
-            if (sessionState.mSession == null && (inputId == null
-                    || sessionState.mInfo.getId().equals(inputId))) {
+            if (sessionState.session == null && (inputId == null
+                    || sessionState.info.getId().equals(inputId))) {
                 sessionsToAbort.add(sessionState);
             }
         }
         for (SessionState sessionState : sessionsToAbort) {
-            removeSessionStateLocked(sessionState.mSessionToken, sessionState.mUserId);
-            sendSessionTokenToClientLocked(sessionState.mClient,
-                    sessionState.mInfo.getId(), null, null, sessionState.mSeq);
+            removeSessionStateLocked(sessionState.sessionToken, sessionState.userId);
+            sendSessionTokenToClientLocked(sessionState.client,
+                    sessionState.info.getId(), null, null, sessionState.seq);
         }
-        updateServiceConnectionLocked(serviceState.mComponent, userId);
+        updateServiceConnectionLocked(serviceState.component, userId);
     }
 
     private ClientState createClientStateLocked(IBinder clientToken, int userId) {
@@ -518,221 +512,26 @@
         return clientState;
     }
 
-    private void createSessionInternalLocked(ITvInputService service, final IBinder sessionToken,
-            final int userId) {
-        final UserState userState = getUserStateLocked(userId);
-        final SessionState sessionState = userState.sessionStateMap.get(sessionToken);
+    private void createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
+            int userId) {
+        UserState userState = getUserStateLocked(userId);
+        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
         if (DEBUG) {
-            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInfo.getId() + ")");
+            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.info.getId() + ")");
         }
-
-        final InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
+        InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
 
         // Set up a callback to send the session token.
-        ITvInputSessionCallback callback = new ITvInputSessionCallback.Stub() {
-            @Override
-            public void onSessionCreated(ITvInputSession session, IBinder harewareSessionToken) {
-                if (DEBUG) {
-                    Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInfo.getId() + ")");
-                }
-                synchronized (mLock) {
-                    sessionState.mSession = session;
-                    sessionState.mHardwareSessionToken = harewareSessionToken;
-                    if (session == null) {
-                        removeSessionStateLocked(sessionToken, userId);
-                        sendSessionTokenToClientLocked(sessionState.mClient,
-                                sessionState.mInfo.getId(), null, null, sessionState.mSeq);
-                    } else {
-                        try {
-                            session.asBinder().linkToDeath(sessionState, 0);
-                        } catch (RemoteException e) {
-                            Slog.e(TAG, "session process has already died", e);
-                        }
-
-                        IBinder clientToken = sessionState.mClient.asBinder();
-                        ClientState clientState = userState.clientStateMap.get(clientToken);
-                        if (clientState == null) {
-                            clientState = createClientStateLocked(clientToken, userId);
-                        }
-                        clientState.mSessionTokens.add(sessionState.mSessionToken);
-
-                        sendSessionTokenToClientLocked(sessionState.mClient,
-                                sessionState.mInfo.getId(), sessionToken, channels[0],
-                                sessionState.mSeq);
-                    }
-                    channels[0].dispose();
-                }
-            }
-
-            @Override
-            public void onChannelRetuned(Uri channelUri) {
-                synchronized (mLock) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
-                    }
-                    if (sessionState.mSession == null || sessionState.mClient == null) {
-                        return;
-                    }
-                    try {
-                        // TODO: Consider adding this channel change in the watch log. When we do
-                        // that, how we can protect the watch log from malicious tv inputs should
-                        // be addressed. e.g. add a field which represents where the channel change
-                        // originated from.
-                        sessionState.mClient.onChannelRetuned(channelUri, sessionState.mSeq);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onChannelRetuned", e);
-                    }
-                }
-            }
-
-            @Override
-            public void onTracksChanged(List<TvTrackInfo> tracks) {
-                synchronized (mLock) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "onTracksChanged(" + tracks + ")");
-                    }
-                    if (sessionState.mSession == null || sessionState.mClient == null) {
-                        return;
-                    }
-                    try {
-                        sessionState.mClient.onTracksChanged(tracks, sessionState.mSeq);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onTracksChanged", e);
-                    }
-                }
-            }
-
-            @Override
-            public void onTrackSelected(int type, String trackId) {
-                synchronized (mLock) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")");
-                    }
-                    if (sessionState.mSession == null || sessionState.mClient == null) {
-                        return;
-                    }
-                    try {
-                        sessionState.mClient.onTrackSelected(type, trackId, sessionState.mSeq);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onTrackSelected", e);
-                    }
-                }
-            }
-
-            @Override
-            public void onVideoAvailable() {
-                synchronized (mLock) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "onVideoAvailable()");
-                    }
-                    if (sessionState.mSession == null || sessionState.mClient == null) {
-                        return;
-                    }
-                    try {
-                        sessionState.mClient.onVideoAvailable(sessionState.mSeq);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onVideoAvailable", e);
-                    }
-                }
-            }
-
-            @Override
-            public void onVideoUnavailable(int reason) {
-                synchronized (mLock) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "onVideoUnavailable(" + reason + ")");
-                    }
-                    if (sessionState.mSession == null || sessionState.mClient == null) {
-                        return;
-                    }
-                    try {
-                        sessionState.mClient.onVideoUnavailable(reason, sessionState.mSeq);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onVideoUnavailable", e);
-                    }
-                }
-            }
-
-            @Override
-            public void onContentAllowed() {
-                synchronized (mLock) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "onContentAllowed()");
-                    }
-                    if (sessionState.mSession == null || sessionState.mClient == null) {
-                        return;
-                    }
-                    try {
-                        sessionState.mClient.onContentAllowed(sessionState.mSeq);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onContentAllowed", e);
-                    }
-                }
-            }
-
-            @Override
-            public void onContentBlocked(String rating) {
-                synchronized (mLock) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "onContentBlocked()");
-                    }
-                    if (sessionState.mSession == null || sessionState.mClient == null) {
-                        return;
-                    }
-                    try {
-                        sessionState.mClient.onContentBlocked(rating, sessionState.mSeq);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onContentBlocked", e);
-                    }
-                }
-            }
-
-            @Override
-            public void onLayoutSurface(int left, int top, int right, int bottom) {
-                synchronized (mLock) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
-                                + ", right=" + right + ", bottom=" + bottom + ",)");
-                    }
-                    if (sessionState.mSession == null || sessionState.mClient == null) {
-                        return;
-                    }
-                    try {
-                        sessionState.mClient.onLayoutSurface(left, top, right, bottom,
-                                sessionState.mSeq);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onLayoutSurface", e);
-                    }
-                }
-            }
-
-            @Override
-            public void onSessionEvent(String eventType, Bundle eventArgs) {
-                synchronized (mLock) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")");
-                    }
-                    if (sessionState.mSession == null || sessionState.mClient == null) {
-                        return;
-                    }
-                    try {
-                        sessionState.mClient.onSessionEvent(eventType, eventArgs,
-                                sessionState.mSeq);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onSessionEvent", e);
-                    }
-                }
-            }
-        };
+        ITvInputSessionCallback callback = new SessionCallback(sessionState, channels);
 
         // Create a session. When failed, send a null token immediately.
         try {
-            service.createSession(channels[1], callback, sessionState.mInfo.getId());
+            service.createSession(channels[1], callback, sessionState.info.getId());
         } catch (RemoteException e) {
             Slog.e(TAG, "error in createSession", e);
             removeSessionStateLocked(sessionToken, userId);
-            sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInfo.getId(), null,
-                    null, sessionState.mSeq);
+            sendSessionTokenToClientLocked(sessionState.client, sessionState.info.getId(), null,
+                    null, sessionState.seq);
         }
         channels[1].dispose();
     }
@@ -748,17 +547,17 @@
 
     private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
         SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
-        if (sessionState.mSession != null) {
+        if (sessionState.session != null) {
             UserState userState = getUserStateLocked(userId);
             if (sessionToken == userState.mainSessionToken) {
                 setMainLocked(sessionToken, false, callingUid, userId);
             }
             try {
-                sessionState.mSession.release();
+                sessionState.session.release();
             } catch (RemoteException e) {
                 Slog.e(TAG, "session process has already died", e);
             }
-            sessionState.mSession = null;
+            sessionState.session = null;
         }
         removeSessionStateLocked(sessionToken, userId);
     }
@@ -781,22 +580,22 @@
 
         // Also remove the session token from the session token list of the current client and
         // service.
-        ClientState clientState = userState.clientStateMap.get(sessionState.mClient.asBinder());
+        ClientState clientState = userState.clientStateMap.get(sessionState.client.asBinder());
         if (clientState != null) {
-            clientState.mSessionTokens.remove(sessionToken);
+            clientState.sessionTokens.remove(sessionToken);
             if (clientState.isEmpty()) {
-                userState.clientStateMap.remove(sessionState.mClient.asBinder());
+                userState.clientStateMap.remove(sessionState.client.asBinder());
             }
         }
 
-        TvInputInfo info = sessionState.mInfo;
+        TvInputInfo info = sessionState.info;
         if (info != null) {
             ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
             if (serviceState != null) {
-                serviceState.mSessionTokens.remove(sessionToken);
+                serviceState.sessionTokens.remove(sessionToken);
             }
         }
-        updateServiceConnectionLocked(sessionState.mInfo.getComponent(), userId);
+        updateServiceConnectionLocked(sessionState.info.getComponent(), userId);
 
         // Log the end of watch.
         SomeArgs args = SomeArgs.obtain();
@@ -807,13 +606,12 @@
 
     private void setMainLocked(IBinder sessionToken, boolean isMain, int callingUid, int userId) {
         SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
-        if (sessionState.mHardwareSessionToken != null) {
-            sessionState = getSessionStateLocked(sessionState.mHardwareSessionToken,
+        if (sessionState.hardwareSessionToken != null) {
+            sessionState = getSessionStateLocked(sessionState.hardwareSessionToken,
                     Process.SYSTEM_UID, userId);
         }
-        ServiceState serviceState = getServiceStateLocked(sessionState.mInfo.getComponent(),
-                userId);
-        if (!serviceState.mIsHardware) {
+        ServiceState serviceState = getServiceStateLocked(sessionState.info.getComponent(), userId);
+        if (!serviceState.isHardware) {
             return;
         }
         ITvInputSession session = getSessionLocked(sessionState);
@@ -876,10 +674,10 @@
     private void setStateLocked(String inputId, int state, int userId) {
         UserState userState = getUserStateLocked(userId);
         TvInputState inputState = userState.inputMap.get(inputId);
-        ServiceState serviceState = userState.serviceStateMap.get(inputState.mInfo.getComponent());
-        int oldState = inputState.mState;
-        inputState.mState = state;
-        if (serviceState != null && serviceState.mService == null
+        ServiceState serviceState = userState.serviceStateMap.get(inputState.info.getComponent());
+        int oldState = inputState.state;
+        inputState.state = state;
+        if (serviceState != null && serviceState.service == null
                 && shouldMaintainConnection(serviceState)) {
             // We don't notify state change while reconnecting. It should remain disconnected.
             return;
@@ -900,7 +698,7 @@
                     UserState userState = getUserStateLocked(resolvedUserId);
                     List<TvInputInfo> inputList = new ArrayList<TvInputInfo>();
                     for (TvInputState state : userState.inputMap.values()) {
-                        inputList.add(state.mInfo);
+                        inputList.add(state.info);
                     }
                     return inputList;
                 }
@@ -918,7 +716,7 @@
                 synchronized (mLock) {
                     UserState userState = getUserStateLocked(resolvedUserId);
                     TvInputState state = userState.inputMap.get(inputId);
-                    return state == null ? null : state.mInfo;
+                    return state == null ? null : state.info;
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -964,8 +762,8 @@
                         Slog.e(TAG, "client process has already died", e);
                     }
                     for (TvInputState state : userState.inputMap.values()) {
-                        notifyInputStateChangedLocked(userState, state.mInfo.getId(),
-                                state.mState, callback);
+                        notifyInputStateChangedLocked(userState, state.info.getId(), state.state,
+                                callback);
                     }
                 }
             } finally {
@@ -1114,14 +912,14 @@
                         sendSessionTokenToClientLocked(client, inputId, null, null, seq);
                         return;
                     }
-                    TvInputInfo info = inputState.mInfo;
+                    TvInputInfo info = inputState.info;
                     ServiceState serviceState = userState.serviceStateMap.get(info.getComponent());
                     if (serviceState == null) {
                         serviceState = new ServiceState(info.getComponent(), resolvedUserId);
                         userState.serviceStateMap.put(info.getComponent(), serviceState);
                     }
                     // Send a null token immediately while reconnecting.
-                    if (serviceState.mReconnecting == true) {
+                    if (serviceState.reconnecting == true) {
                         sendSessionTokenToClientLocked(client, inputId, null, null, seq);
                         return;
                     }
@@ -1135,10 +933,10 @@
                     userState.sessionStateMap.put(sessionToken, sessionState);
 
                     // Also, add them to the session state map of the current service.
-                    serviceState.mSessionTokens.add(sessionToken);
+                    serviceState.sessionTokens.add(sessionToken);
 
-                    if (serviceState.mService != null) {
-                        createSessionInternalLocked(serviceState.mService, sessionToken,
+                    if (serviceState.service != null) {
+                        createSessionInternalLocked(serviceState.service, sessionToken,
                                 resolvedUserId);
                     } else {
                         updateServiceConnectionLocked(info.getComponent(), resolvedUserId);
@@ -1213,10 +1011,10 @@
                     try {
                         SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
                                 resolvedUserId);
-                        if (sessionState.mHardwareSessionToken == null) {
+                        if (sessionState.hardwareSessionToken == null) {
                             getSessionLocked(sessionState).setSurface(surface);
                         } else {
-                            getSessionLocked(sessionState.mHardwareSessionToken,
+                            getSessionLocked(sessionState.hardwareSessionToken,
                                     Process.SYSTEM_UID, resolvedUserId).setSurface(surface);
                         }
                     } catch (RemoteException e) {
@@ -1244,9 +1042,10 @@
                     try {
                         SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
                                 resolvedUserId);
-                        getSessionLocked(sessionState).dispatchSurfaceChanged(format, width, height);
-                        if (sessionState.mHardwareSessionToken != null) {
-                            getSessionLocked(sessionState.mHardwareSessionToken, Process.SYSTEM_UID,
+                        getSessionLocked(sessionState).dispatchSurfaceChanged(format, width,
+                                height);
+                        if (sessionState.hardwareSessionToken != null) {
+                            getSessionLocked(sessionState.hardwareSessionToken, Process.SYSTEM_UID,
                                     resolvedUserId).dispatchSurfaceChanged(format, width, height);
                         }
                     } catch (RemoteException e) {
@@ -1272,10 +1071,10 @@
                         SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
                                 resolvedUserId);
                         getSessionLocked(sessionState).setVolume(volume);
-                        if (sessionState.mHardwareSessionToken != null) {
+                        if (sessionState.hardwareSessionToken != null) {
                             // Here, we let the hardware session know only whether volume is on or
                             // off to prevent that the volume is controlled in the both side.
-                            getSessionLocked(sessionState.mHardwareSessionToken,
+                            getSessionLocked(sessionState.hardwareSessionToken,
                                     Process.SYSTEM_UID, resolvedUserId).setVolume((volume > 0.0f)
                                             ? REMOTE_VOLUME_ON : REMOTE_VOLUME_OFF);
                         }
@@ -1309,7 +1108,7 @@
 
                         // Log the start of watch.
                         SomeArgs args = SomeArgs.obtain();
-                        args.arg1 = sessionState.mInfo.getComponent().getPackageName();
+                        args.arg1 = sessionState.info.getComponent().getPackageName();
                         args.arg2 = System.currentTimeMillis();
                         args.arg3 = ContentUris.parseId(channelUri);
                         args.arg4 = params;
@@ -1569,10 +1368,10 @@
                         return false;
                     }
                     for (SessionState sessionState : userState.sessionStateMap.values()) {
-                        if (sessionState.mInfo.getId().equals(inputId)
-                                && sessionState.mHardwareSessionToken != null) {
+                        if (sessionState.info.getId().equals(inputId)
+                                && sessionState.hardwareSessionToken != null) {
                             hardwareInputId = userState.sessionStateMap.get(
-                                    sessionState.mHardwareSessionToken).mInfo.getId();
+                                    sessionState.hardwareSessionToken).info.getId();
                             break;
                         }
                     }
@@ -1601,8 +1400,8 @@
                         SessionState[] sessionStates = userState.sessionStateMap.values().toArray(
                                 new SessionState[0]);
                         // Check if there is a wrapper input.
-                        if (sessionStates[0].mHardwareSessionToken != null
-                                || sessionStates[1].mHardwareSessionToken != null) {
+                        if (sessionStates[0].hardwareSessionToken != null
+                                || sessionStates[1].hardwareSessionToken != null) {
                             return true;
                         }
                     }
@@ -1662,15 +1461,15 @@
 
                         pw.increaseIndent();
 
-                        pw.println("mSessionTokens:");
+                        pw.println("sessionTokens:");
                         pw.increaseIndent();
-                        for (IBinder token : client.mSessionTokens) {
+                        for (IBinder token : client.sessionTokens) {
                             pw.println("" + token);
                         }
                         pw.decreaseIndent();
 
-                        pw.println("mClientTokens: " + client.mClientToken);
-                        pw.println("mUserId: " + client.mUserId);
+                        pw.println("clientTokens: " + client.clientToken);
+                        pw.println("userId: " + client.userId);
 
                         pw.decreaseIndent();
                     }
@@ -1685,17 +1484,17 @@
 
                         pw.increaseIndent();
 
-                        pw.println("mSessionTokens:");
+                        pw.println("sessionTokens:");
                         pw.increaseIndent();
-                        for (IBinder token : service.mSessionTokens) {
+                        for (IBinder token : service.sessionTokens) {
                             pw.println("" + token);
                         }
                         pw.decreaseIndent();
 
-                        pw.println("mService: " + service.mService);
-                        pw.println("mCallback: " + service.mCallback);
-                        pw.println("mBound: " + service.mBound);
-                        pw.println("mReconnecting: " + service.mReconnecting);
+                        pw.println("service: " + service.service);
+                        pw.println("callback: " + service.callback);
+                        pw.println("bound: " + service.bound);
+                        pw.println("reconnecting: " + service.reconnecting);
 
                         pw.decreaseIndent();
                     }
@@ -1709,15 +1508,15 @@
                         pw.println(entry.getKey() + ": " + session);
 
                         pw.increaseIndent();
-                        pw.println("mInfo: " + session.mInfo);
-                        pw.println("mClient: " + session.mClient);
-                        pw.println("mSeq: " + session.mSeq);
-                        pw.println("mCallingUid: " + session.mCallingUid);
-                        pw.println("mUserId: " + session.mUserId);
-                        pw.println("mSessionToken: " + session.mSessionToken);
-                        pw.println("mSession: " + session.mSession);
-                        pw.println("mLogUri: " + session.mLogUri);
-                        pw.println("mHardwareSessionToken: " + session.mHardwareSessionToken);
+                        pw.println("info: " + session.info);
+                        pw.println("client: " + session.client);
+                        pw.println("seq: " + session.seq);
+                        pw.println("callingUid: " + session.callingUid);
+                        pw.println("userId: " + session.userId);
+                        pw.println("sessionToken: " + session.sessionToken);
+                        pw.println("session: " + session.session);
+                        pw.println("logUri: " + session.logUri);
+                        pw.println("hardwareSessionToken: " + session.hardwareSessionToken);
                         pw.decreaseIndent();
                     }
                     pw.decreaseIndent();
@@ -1736,19 +1535,6 @@
         }
     }
 
-    private static final class TvInputState {
-        // A TvInputInfo object which represents the TV input.
-        private TvInputInfo mInfo;
-
-        // The state of TV input. Connected by default.
-        private int mState = INPUT_STATE_CONNECTED;
-
-        @Override
-        public String toString() {
-            return "mInfo: " + mInfo + "; mState: " + mState;
-        }
-    }
-
     private static final class UserState {
         // A mapping from the TV input id to its TvInputState.
         private Map<String, TvInputState> inputMap = new HashMap<String, TvInputState>();
@@ -1789,104 +1575,117 @@
     }
 
     private final class ClientState implements IBinder.DeathRecipient {
-        private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
+        private final List<IBinder> sessionTokens = new ArrayList<IBinder>();
 
-        private IBinder mClientToken;
-        private final int mUserId;
+        private IBinder clientToken;
+        private final int userId;
 
         ClientState(IBinder clientToken, int userId) {
-            mClientToken = clientToken;
-            mUserId = userId;
+            this.clientToken = clientToken;
+            this.userId = userId;
         }
 
         public boolean isEmpty() {
-            return mSessionTokens.isEmpty();
+            return sessionTokens.isEmpty();
         }
 
         @Override
         public void binderDied() {
             synchronized (mLock) {
-                UserState userState = getUserStateLocked(mUserId);
+                UserState userState = getUserStateLocked(userId);
                 // DO NOT remove the client state of clientStateMap in this method. It will be
                 // removed in releaseSessionLocked().
-                ClientState clientState = userState.clientStateMap.get(mClientToken);
+                ClientState clientState = userState.clientStateMap.get(clientToken);
                 if (clientState != null) {
-                    while (clientState.mSessionTokens.size() > 0) {
+                    while (clientState.sessionTokens.size() > 0) {
                         releaseSessionLocked(
-                                clientState.mSessionTokens.get(0), Process.SYSTEM_UID, mUserId);
+                                clientState.sessionTokens.get(0), Process.SYSTEM_UID, userId);
                     }
                 }
-                mClientToken = null;
+                clientToken = null;
             }
         }
     }
 
     private final class ServiceState {
-        private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
-        private final ServiceConnection mConnection;
-        private final ComponentName mComponent;
-        private final boolean mIsHardware;
-        private final List<TvInputInfo> mInputList = new ArrayList<TvInputInfo>();
+        private final List<IBinder> sessionTokens = new ArrayList<IBinder>();
+        private final ServiceConnection connection;
+        private final ComponentName component;
+        private final boolean isHardware;
+        private final List<TvInputInfo> inputList = new ArrayList<TvInputInfo>();
 
-        private ITvInputService mService;
-        private ServiceCallback mCallback;
-        private boolean mBound;
-        private boolean mReconnecting;
+        private ITvInputService service;
+        private ServiceCallback callback;
+        private boolean bound;
+        private boolean reconnecting;
 
         private ServiceState(ComponentName component, int userId) {
-            mComponent = component;
-            mConnection = new InputServiceConnection(component, userId);
-            mIsHardware = hasHardwarePermission(mContext.getPackageManager(), mComponent);
+            this.component = component;
+            this.connection = new InputServiceConnection(component, userId);
+            this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component);
+        }
+    }
+
+    private static final class TvInputState {
+        // A TvInputInfo object which represents the TV input.
+        private TvInputInfo info;
+
+        // The state of TV input. Connected by default.
+        private int state = INPUT_STATE_CONNECTED;
+
+        @Override
+        public String toString() {
+            return "info: " + info + "; state: " + state;
         }
     }
 
     private final class SessionState implements IBinder.DeathRecipient {
-        private final TvInputInfo mInfo;
-        private final ITvInputClient mClient;
-        private final int mSeq;
-        private final int mCallingUid;
-        private final int mUserId;
-        private final IBinder mSessionToken;
-        private ITvInputSession mSession;
-        private Uri mLogUri;
+        private final TvInputInfo info;
+        private final ITvInputClient client;
+        private final int seq;
+        private final int callingUid;
+        private final int userId;
+        private final IBinder sessionToken;
+        private ITvInputSession session;
+        private Uri logUri;
         // Not null if this session represents an external device connected to a hardware TV input.
-        private IBinder mHardwareSessionToken;
+        private IBinder hardwareSessionToken;
 
         private SessionState(IBinder sessionToken, TvInputInfo info, ITvInputClient client,
                 int seq, int callingUid, int userId) {
-            mSessionToken = sessionToken;
-            mInfo = info;
-            mClient = client;
-            mSeq = seq;
-            mCallingUid = callingUid;
-            mUserId = userId;
+            this.sessionToken = sessionToken;
+            this.info = info;
+            this.client = client;
+            this.seq = seq;
+            this.callingUid = callingUid;
+            this.userId = userId;
         }
 
         @Override
         public void binderDied() {
             synchronized (mLock) {
-                mSession = null;
-                if (mClient != null) {
+                session = null;
+                if (client != null) {
                     try {
-                        mClient.onSessionReleased(mSeq);
+                        client.onSessionReleased(seq);
                     } catch(RemoteException e) {
                         Slog.e(TAG, "error in onSessionReleased", e);
                     }
                 }
                 // If there are any other sessions based on this session, they should be released.
-                UserState userState = getUserStateLocked(mUserId);
+                UserState userState = getUserStateLocked(userId);
                 for (SessionState sessionState : userState.sessionStateMap.values()) {
-                    if (mSessionToken == sessionState.mHardwareSessionToken) {
-                        releaseSessionLocked(sessionState.mSessionToken, Process.SYSTEM_UID,
-                                mUserId);
+                    if (sessionToken == sessionState.hardwareSessionToken) {
+                        releaseSessionLocked(sessionState.sessionToken, Process.SYSTEM_UID,
+                                userId);
                         try {
-                            sessionState.mClient.onSessionReleased(sessionState.mSeq);
+                            sessionState.client.onSessionReleased(sessionState.seq);
                         } catch (RemoteException e) {
                             Slog.e(TAG, "error in onSessionReleased", e);
                         }
                     }
                 }
-                removeSessionStateLocked(mSessionToken, mUserId);
+                removeSessionStateLocked(sessionToken, userId);
             }
         }
     }
@@ -1908,37 +1707,37 @@
             synchronized (mLock) {
                 UserState userState = getUserStateLocked(mUserId);
                 ServiceState serviceState = userState.serviceStateMap.get(mComponent);
-                serviceState.mService = ITvInputService.Stub.asInterface(service);
+                serviceState.service = ITvInputService.Stub.asInterface(service);
 
                 // Register a callback, if we need to.
-                if (serviceState.mIsHardware && serviceState.mCallback == null) {
-                    serviceState.mCallback = new ServiceCallback(mComponent, mUserId);
+                if (serviceState.isHardware && serviceState.callback == null) {
+                    serviceState.callback = new ServiceCallback(mComponent, mUserId);
                     try {
-                        serviceState.mService.registerCallback(serviceState.mCallback);
+                        serviceState.service.registerCallback(serviceState.callback);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in registerCallback", e);
                     }
                 }
 
                 // And create sessions, if any.
-                for (IBinder sessionToken : serviceState.mSessionTokens) {
-                    createSessionInternalLocked(serviceState.mService, sessionToken, mUserId);
+                for (IBinder sessionToken : serviceState.sessionTokens) {
+                    createSessionInternalLocked(serviceState.service, sessionToken, mUserId);
                 }
 
                 for (TvInputState inputState : userState.inputMap.values()) {
-                    if (inputState.mInfo.getComponent().equals(component)
-                            && inputState.mState != INPUT_STATE_DISCONNECTED) {
-                        notifyInputStateChangedLocked(userState, inputState.mInfo.getId(),
-                                inputState.mState, null);
+                    if (inputState.info.getComponent().equals(component)
+                            && inputState.state != INPUT_STATE_DISCONNECTED) {
+                        notifyInputStateChangedLocked(userState, inputState.info.getId(),
+                                inputState.state, null);
                     }
                 }
 
-                if (serviceState.mIsHardware) {
+                if (serviceState.isHardware) {
                     List<TvInputHardwareInfo> hardwareInfoList =
                             mTvInputHardwareManager.getHardwareList();
                     for (TvInputHardwareInfo hardwareInfo : hardwareInfoList) {
                         try {
-                            serviceState.mService.notifyHardwareAdded(hardwareInfo);
+                            serviceState.service.notifyHardwareAdded(hardwareInfo);
                         } catch (RemoteException e) {
                             Slog.e(TAG, "error in notifyHardwareAdded", e);
                         }
@@ -1948,7 +1747,7 @@
                             mTvInputHardwareManager.getHdmiDeviceList();
                     for (HdmiDeviceInfo deviceInfo : deviceInfoList) {
                         try {
-                            serviceState.mService.notifyHdmiDeviceAdded(deviceInfo);
+                            serviceState.service.notifyHdmiDeviceAdded(deviceInfo);
                         } catch (RemoteException e) {
                             Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
                         }
@@ -1970,16 +1769,16 @@
                 UserState userState = getUserStateLocked(mUserId);
                 ServiceState serviceState = userState.serviceStateMap.get(mComponent);
                 if (serviceState != null) {
-                    serviceState.mReconnecting = true;
-                    serviceState.mBound = false;
-                    serviceState.mService = null;
-                    serviceState.mCallback = null;
+                    serviceState.reconnecting = true;
+                    serviceState.bound = false;
+                    serviceState.service = null;
+                    serviceState.callback = null;
 
                     abortPendingCreateSessionRequestsLocked(serviceState, null, mUserId);
 
                     for (TvInputState inputState : userState.inputMap.values()) {
-                        if (inputState.mInfo.getComponent().equals(component)) {
-                            notifyInputStateChangedLocked(userState, inputState.mInfo.getId(),
+                        if (inputState.info.getComponent().equals(component)) {
+                            notifyInputStateChangedLocked(userState, inputState.info.getId(),
                                     INPUT_STATE_DISCONNECTED, null);
                         }
                     }
@@ -2012,7 +1811,7 @@
 
         private void addTvInputLocked(TvInputInfo inputInfo) {
             ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
-            serviceState.mInputList.add(inputInfo);
+            serviceState.inputList.add(inputInfo);
             buildTvInputListLocked(mUserId);
         }
 
@@ -2042,7 +1841,7 @@
             synchronized (mLock) {
                 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
                 boolean removed = false;
-                for (Iterator<TvInputInfo> it = serviceState.mInputList.iterator();
+                for (Iterator<TvInputInfo> it = serviceState.inputList.iterator();
                         it.hasNext(); ) {
                     if (it.next().getId().equals(inputId)) {
                         it.remove();
@@ -2060,7 +1859,211 @@
         }
     }
 
-    private final class WatchLogHandler extends Handler {
+    private final class SessionCallback extends ITvInputSessionCallback.Stub {
+        private final SessionState sessionState;
+        private final InputChannel[] mChannels;
+
+        SessionCallback(SessionState sessionState, InputChannel[] channels) {
+            this.sessionState = sessionState;
+            mChannels = channels;
+        }
+
+        @Override
+        public void onSessionCreated(ITvInputSession session, IBinder harewareSessionToken) {
+            if (DEBUG) {
+                Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.info.getId() + ")");
+            }
+            synchronized (mLock) {
+                sessionState.session = session;
+                sessionState.hardwareSessionToken = harewareSessionToken;
+                if (session == null) {
+                    removeSessionStateLocked(sessionState.sessionToken, sessionState.userId);
+                    sendSessionTokenToClientLocked(sessionState.client,
+                            sessionState.info.getId(), null, null, sessionState.seq);
+                } else {
+                    try {
+                        session.asBinder().linkToDeath(sessionState, 0);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "session process has already died", e);
+                    }
+
+                    IBinder clientToken = sessionState.client.asBinder();
+                    UserState userState = getUserStateLocked(sessionState.userId);
+                    ClientState clientState = userState.clientStateMap.get(clientToken);
+                    if (clientState == null) {
+                        clientState = createClientStateLocked(clientToken, sessionState.userId);
+                    }
+                    clientState.sessionTokens.add(sessionState.sessionToken);
+
+                    sendSessionTokenToClientLocked(sessionState.client,
+                            sessionState.info.getId(), sessionState.sessionToken, mChannels[0],
+                            sessionState.seq);
+                }
+                mChannels[0].dispose();
+            }
+        }
+
+        @Override
+        public void onChannelRetuned(Uri channelUri) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
+                }
+                if (sessionState.session == null || sessionState.client == null) {
+                    return;
+                }
+                try {
+                    // TODO: Consider adding this channel change in the watch log. When we do
+                    // that, how we can protect the watch log from malicious tv inputs should
+                    // be addressed. e.g. add a field which represents where the channel change
+                    // originated from.
+                    sessionState.client.onChannelRetuned(channelUri, sessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onChannelRetuned", e);
+                }
+            }
+        }
+
+        @Override
+        public void onTracksChanged(List<TvTrackInfo> tracks) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onTracksChanged(" + tracks + ")");
+                }
+                if (sessionState.session == null || sessionState.client == null) {
+                    return;
+                }
+                try {
+                    sessionState.client.onTracksChanged(tracks, sessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onTracksChanged", e);
+                }
+            }
+        }
+
+        @Override
+        public void onTrackSelected(int type, String trackId) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")");
+                }
+                if (sessionState.session == null || sessionState.client == null) {
+                    return;
+                }
+                try {
+                    sessionState.client.onTrackSelected(type, trackId, sessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onTrackSelected", e);
+                }
+            }
+        }
+
+        @Override
+        public void onVideoAvailable() {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onVideoAvailable()");
+                }
+                if (sessionState.session == null || sessionState.client == null) {
+                    return;
+                }
+                try {
+                    sessionState.client.onVideoAvailable(sessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onVideoAvailable", e);
+                }
+            }
+        }
+
+        @Override
+        public void onVideoUnavailable(int reason) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onVideoUnavailable(" + reason + ")");
+                }
+                if (sessionState.session == null || sessionState.client == null) {
+                    return;
+                }
+                try {
+                    sessionState.client.onVideoUnavailable(reason, sessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onVideoUnavailable", e);
+                }
+            }
+        }
+
+        @Override
+        public void onContentAllowed() {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onContentAllowed()");
+                }
+                if (sessionState.session == null || sessionState.client == null) {
+                    return;
+                }
+                try {
+                    sessionState.client.onContentAllowed(sessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onContentAllowed", e);
+                }
+            }
+        }
+
+        @Override
+        public void onContentBlocked(String rating) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onContentBlocked()");
+                }
+                if (sessionState.session == null || sessionState.client == null) {
+                    return;
+                }
+                try {
+                    sessionState.client.onContentBlocked(rating, sessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onContentBlocked", e);
+                }
+            }
+        }
+
+        @Override
+        public void onLayoutSurface(int left, int top, int right, int bottom) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top
+                            + ", right=" + right + ", bottom=" + bottom + ",)");
+                }
+                if (sessionState.session == null || sessionState.client == null) {
+                    return;
+                }
+                try {
+                    sessionState.client.onLayoutSurface(left, top, right, bottom, sessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onLayoutSurface", e);
+                }
+            }
+        }
+
+        @Override
+        public void onSessionEvent(String eventType, Bundle eventArgs) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")");
+                }
+                if (sessionState.session == null || sessionState.client == null) {
+                    return;
+                }
+                try {
+                    sessionState.client.onSessionEvent(eventType, eventArgs,
+                            sessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onSessionEvent", e);
+                }
+            }
+        }
+    }
+
+    private static final class WatchLogHandler extends Handler {
         // There are only two kinds of watch events that can happen on the system:
         // 1. The current TV input session is tuned to a new channel.
         // 2. The session is released for some reason.
@@ -2072,8 +2075,11 @@
         private static final int MSG_LOG_WATCH_START = 1;
         private static final int MSG_LOG_WATCH_END = 2;
 
-        public WatchLogHandler(Looper looper) {
+        private final ContentResolver mContentResolver;
+
+        public WatchLogHandler(ContentResolver contentResolver, Looper looper) {
             super(looper);
+            mContentResolver = contentResolver;
         }
 
         @Override
@@ -2159,7 +2165,7 @@
         }
     }
 
-    final class HardwareListener implements TvInputHardwareManager.Listener {
+    private final class HardwareListener implements TvInputHardwareManager.Listener {
         @Override
         public void onStateChanged(String inputId, int state) {
             synchronized (mLock) {
@@ -2173,9 +2179,9 @@
                 UserState userState = getUserStateLocked(mCurrentUserId);
                 // Broadcast the event to all hardware inputs.
                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
-                    if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+                    if (!serviceState.isHardware || serviceState.service == null) continue;
                     try {
-                        serviceState.mService.notifyHardwareAdded(info);
+                        serviceState.service.notifyHardwareAdded(info);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in notifyHardwareAdded", e);
                     }
@@ -2189,9 +2195,9 @@
                 UserState userState = getUserStateLocked(mCurrentUserId);
                 // Broadcast the event to all hardware inputs.
                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
-                    if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+                    if (!serviceState.isHardware || serviceState.service == null) continue;
                     try {
-                        serviceState.mService.notifyHardwareRemoved(info);
+                        serviceState.service.notifyHardwareRemoved(info);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in notifyHardwareRemoved", e);
                     }
@@ -2205,9 +2211,9 @@
                 UserState userState = getUserStateLocked(mCurrentUserId);
                 // Broadcast the event to all hardware inputs.
                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
-                    if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+                    if (!serviceState.isHardware || serviceState.service == null) continue;
                     try {
-                        serviceState.mService.notifyHdmiDeviceAdded(deviceInfo);
+                        serviceState.service.notifyHdmiDeviceAdded(deviceInfo);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
                     }
@@ -2221,9 +2227,9 @@
                 UserState userState = getUserStateLocked(mCurrentUserId);
                 // Broadcast the event to all hardware inputs.
                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
-                    if (!serviceState.mIsHardware || serviceState.mService == null) continue;
+                    if (!serviceState.isHardware || serviceState.service == null) continue;
                     try {
-                        serviceState.mService.notifyHdmiDeviceRemoved(deviceInfo);
+                        serviceState.service.notifyHdmiDeviceRemoved(deviceInfo);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e);
                     }
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index ef74205..69c9144 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -114,6 +114,10 @@
         transformation.clear();
         transformation.setAlpha(mAppToken.isVisible() ? 1 : 0);
         hasTransformation = true;
+
+        if (!mAppToken.appFullscreen) {
+            anim.setBackgroundColor(0);
+        }
     }
 
     public void setDummyAnimation() {
diff --git a/telecomm/java/android/telecomm/PhoneAccount.java b/telecomm/java/android/telecomm/PhoneAccount.java
index 411f48c..97a7b9d 100644
--- a/telecomm/java/android/telecomm/PhoneAccount.java
+++ b/telecomm/java/android/telecomm/PhoneAccount.java
@@ -93,7 +93,7 @@
         private CharSequence mLabel;
         private CharSequence mShortDescription;
 
-        private Builder() {}
+        public Builder() {}
 
         public Builder withAccountHandle(PhoneAccountHandle value) {
             this.mAccountHandle = value;
diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java
index 868282f..2243288 100644
--- a/telecomm/java/android/telecomm/TelecommManager.java
+++ b/telecomm/java/android/telecomm/TelecommManager.java
@@ -285,6 +285,20 @@
     }
 
     /**
+     * Sets the default account for making outgoing phone calls.
+     * @hide
+     */
+    public void setDefaultOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
+        try {
+            if (isServiceConnected()) {
+                getTelecommService().setDefaultOutgoingPhoneAccount(accountHandle);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelecommService#setDefaultOutgoingPhoneAccount");
+        }
+    }
+
+    /**
      * Return a list of {@link PhoneAccountHandle}s which can be used to make and receive phone
      * calls.
      *
@@ -303,6 +317,55 @@
     }
 
     /**
+     * Returns the current SIM call manager. Apps must be prepared for this method to return
+     * {@code null}, indicating that there currently exists no user-chosen default
+     * {@code PhoneAccount}.
+     * @return The phone account handle of the current sim call manager.
+     * @hide
+     */
+    public PhoneAccountHandle getSimCallManager() {
+        try {
+            if (isServiceConnected()) {
+                return getTelecommService().getSimCallManager();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelecommService#getSimCallManager");
+        }
+        return null;
+    }
+
+    /**
+     * Sets the SIM call manager to the specified phone account.
+     * @param accountHandle The phone account handle of the account to set as the sim call manager.
+     * @hide
+     */
+    public void setSimCallManager(PhoneAccountHandle accountHandle) {
+        try {
+            if (isServiceConnected()) {
+                getTelecommService().setSimCallManager(accountHandle);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelecommService#setSimCallManager");
+        }
+    }
+
+    /**
+     * Returns the list of registered SIM call managers.
+     * @return List of registered SIM call managers.
+     * @hide
+     */
+    public List<PhoneAccountHandle> getSimCallManagers() {
+        try {
+            if (isServiceConnected()) {
+                return getTelecommService().getSimCallManagers();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelecommService#getSimCallManagers");
+        }
+        return new ArrayList<>();
+    }
+
+    /**
      * Determine whether the device has more than one account registered and enabled.
      *
      * @return {@code true} if the device has more than one account registered and enabled and
@@ -614,4 +677,4 @@
         }
         return isConnected;
     }
-}
+}
\ No newline at end of file
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index 0ac5078..131307a 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -35,37 +35,57 @@
     void showInCallScreen(boolean showDialpad);
 
     /**
-     * @see TelecommManager#getDefaultOutgoingPhoneAccount
+     * @see TelecommServiceImpl#getDefaultOutgoingPhoneAccount
      */
     PhoneAccountHandle getDefaultOutgoingPhoneAccount();
 
     /**
-     * @see TelecommManager#getOutgoingPhoneAccounts
+     * @see TelecommServiceImpl#setDefaultOutgoingPhoneAccount
+     */
+    void setDefaultOutgoingPhoneAccount(in PhoneAccountHandle account);
+
+    /**
+     * @see TelecommServiceImpl#getOutgoingPhoneAccounts
      */
     List<PhoneAccountHandle> getOutgoingPhoneAccounts();
 
     /**
-     * @see TelecommManager#getPhoneAccount
+     * @see TelecommServiceImpl#getPhoneAccount
      */
     PhoneAccount getPhoneAccount(in PhoneAccountHandle account);
 
     /**
-     * @see TelecommManager#registerPhoneAccount
+     * @see TelecommServiceImpl#getSimCallManager
+     */
+    PhoneAccountHandle getSimCallManager();
+
+    /**
+     * @see TelecommServiceImpl#setSimCallManager
+     */
+    void setSimCallManager(in PhoneAccountHandle account);
+
+    /**
+     * @see TelecommServiceImpl#getSimCallManagers
+     */
+    List<PhoneAccountHandle> getSimCallManagers();
+
+    /**
+     * @see TelecommServiceImpl#registerPhoneAccount
      */
     void registerPhoneAccount(in PhoneAccount metadata);
 
     /**
-     * @see TelecommManager#unregisterPhoneAccount
+     * @see TelecommServiceImpl#unregisterPhoneAccount
      */
     void unregisterPhoneAccount(in PhoneAccountHandle account);
 
     /**
-     * @see TelecommManager#clearAccounts
+     * @see TelecommServiceImpl#clearAccounts
      */
     void clearAccounts(String packageName);
 
     /**
-     * @see TelecommManager#getDefaultPhoneApp
+     * @see TelecommServiceImpl#getDefaultPhoneApp
      */
     ComponentName getDefaultPhoneApp();
 
@@ -74,52 +94,52 @@
     //
 
     /**
-     * @see TelecommManager#silenceRinger
+     * @see TelecommServiceImpl#silenceRinger
      */
     void silenceRinger();
 
     /**
-     * @see TelecommManager#isInCall
+     * @see TelecommServiceImpl#isInCall
      */
     boolean isInCall();
 
     /**
-     * @see TelecomManager#isRinging
+     * @see TelecommServiceImpl#isRinging
      */
     boolean isRinging();
 
     /**
-     * @see TelecommManager#endCall
+     * @see TelecommServiceImpl#endCall
      */
     boolean endCall();
 
     /**
-     * @see TelecommManager#acceptRingingCall
+     * @see TelecommServiceImpl#acceptRingingCall
      */
     void acceptRingingCall();
 
     /**
-     * @see TelecommManager#cancelMissedCallsNotification
+     * @see TelecommServiceImpl#cancelMissedCallsNotification
      */
     void cancelMissedCallsNotification();
 
     /**
-     * @see TelecommManager#handleMmi
+     * @see TelecommServiceImpl#handleMmi
      */
     boolean handlePinMmi(String dialString);
 
     /**
-     * @see TelecomManager#isTtySupported
+     * @see TelecommServiceImpl#isTtySupported
      */
     boolean isTtySupported();
 
     /**
-     * @see TelecomManager#getCurrentTtyMode
+     * @see TelecommServiceImpl#getCurrentTtyMode
      */
     int getCurrentTtyMode();
 
     /**
-     * @see TelecommManager#addNewIncomingCall
+     * @see TelecommServiceImpl#addNewIncomingCall
      */
     void addNewIncomingCall(in PhoneAccountHandle phoneAccount, in Bundle extras);
 }
diff --git a/telecomm/java/com/android/internal/telecomm/RemoteServiceCallback.aidl b/telecomm/java/com/android/internal/telecomm/RemoteServiceCallback.aidl
index 42c77d7..0ab7564 100644
--- a/telecomm/java/com/android/internal/telecomm/RemoteServiceCallback.aidl
+++ b/telecomm/java/com/android/internal/telecomm/RemoteServiceCallback.aidl
@@ -20,6 +20,8 @@
 
 /**
  * Simple response callback object.
+ *
+ * {@hide}
  */
 oneway interface RemoteServiceCallback {
     void onError();
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index c84f40e..cd7d178 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -617,21 +617,25 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
     @Override
     public KeySet getKeySetByAlias(String packageName, String alias) {
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
     @Override
     public KeySet getSigningKeySet(String packageName) {
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
     @Override
     public boolean isSignedBy(String packageName, KeySet ks) {
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
     @Override
     public boolean isSignedByExactly(String packageName, KeySet ks) {
         throw new UnsupportedOperationException();
diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
index b6591bd..c08c1a3 100644
--- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
+++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
@@ -23,7 +23,6 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.text.format.DateUtils;
-import android.util.ArrayMap;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -36,6 +35,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Map;
 
 public class UsageStatsActivity extends ListActivity {
     private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14;
@@ -84,7 +84,7 @@
     private void updateAdapter() {
         long now = System.currentTimeMillis();
         long beginTime = now - USAGE_STATS_PERIOD;
-        ArrayMap<String, UsageStats> stats = mUsageStatsManager.queryAndAggregateUsageStats(
+        Map<String, UsageStats> stats = mUsageStatsManager.queryAndAggregateUsageStats(
                 beginTime, now);
         mAdapter.update(stats);
     }
@@ -92,17 +92,13 @@
     private class Adapter extends BaseAdapter {
         private ArrayList<UsageStats> mStats = new ArrayList<>();
 
-        public void update(ArrayMap<String, UsageStats> stats) {
+        public void update(Map<String, UsageStats> stats) {
             mStats.clear();
             if (stats == null) {
                 return;
             }
 
-            final int packageCount = stats.size();
-            for (int i = 0; i < packageCount; i++) {
-                mStats.add(stats.valueAt(i));
-            }
-
+            mStats.addAll(stats.values());
             Collections.sort(mStats, mComparator);
             notifyDataSetChanged();
         }
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
index e1a579c..1f01461 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
@@ -92,8 +92,7 @@
                 break;
             case AlwaysOnHotwordDetector.STATE_KEYPHRASE_UNENROLLED:
                 Log.i(TAG, "STATE_KEYPHRASE_UNENROLLED");
-                Intent enroll = mHotwordDetector.getManageIntent(
-                        AlwaysOnHotwordDetector.MANAGE_ACTION_ENROLL);
+                Intent enroll = mHotwordDetector.createIntentToEnroll();
                 Log.i(TAG, "Need to enroll with " + enroll);
                 break;
             case AlwaysOnHotwordDetector.STATE_KEYPHRASE_ENROLLED:
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index b44e2d1..117fc24 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -1594,6 +1594,11 @@
     return mIncludedAssets.getResources(false);
 }
 
+AssetManager& AaptAssets::getAssetManager()
+{
+    return mIncludedAssets;
+}
+
 void AaptAssets::print(const String8& prefix) const
 {
     String8 innerPrefix(prefix);
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 0c2576a..3fc9f81 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -561,6 +561,7 @@
     status_t buildIncludedResources(Bundle* bundle);
     status_t addIncludedResources(const sp<AaptFile>& file);
     const ResTable& getIncludedResources() const;
+    AssetManager& getAssetManager();
 
     void print(const String8& prefix) const;
 
diff --git a/tools/aapt/AaptXml.cpp b/tools/aapt/AaptXml.cpp
new file mode 100644
index 0000000..708e405
--- /dev/null
+++ b/tools/aapt/AaptXml.cpp
@@ -0,0 +1,184 @@
+/*
+ * 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.
+ */
+
+#include <androidfw/ResourceTypes.h>
+#include <utils/String8.h>
+
+#include "AaptXml.h"
+
+using namespace android;
+
+namespace AaptXml {
+
+static String8 getStringAttributeAtIndex(const ResXMLTree& tree, ssize_t attrIndex,
+        String8* outError) {
+    Res_value value;
+    if (tree.getAttributeValue(attrIndex, &value) < 0) {
+        if (outError != NULL) {
+            *outError = "could not find attribute at index";
+        }
+        return String8();
+    }
+
+    if (value.dataType != Res_value::TYPE_STRING) {
+        if (outError != NULL) {
+            *outError = "attribute is not a string value";
+        }
+        return String8();
+    }
+
+    size_t len;
+    const uint16_t* str = tree.getAttributeStringValue(attrIndex, &len);
+    return str ? String8(str, len) : String8();
+}
+
+static int32_t getIntegerAttributeAtIndex(const ResXMLTree& tree, ssize_t attrIndex,
+    int32_t defValue, String8* outError) {
+    Res_value value;
+    if (tree.getAttributeValue(attrIndex, &value) < 0) {
+        if (outError != NULL) {
+            *outError = "could not find attribute at index";
+        }
+        return defValue;
+    }
+
+    if (value.dataType < Res_value::TYPE_FIRST_INT
+            || value.dataType > Res_value::TYPE_LAST_INT) {
+        if (outError != NULL) {
+            *outError = "attribute is not an integer value";
+        }
+        return defValue;
+    }
+    return value.data;
+}
+
+
+ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes) {
+    size_t attrCount = tree.getAttributeCount();
+    for (size_t i = 0; i < attrCount; i++) {
+        if (tree.getAttributeNameResID(i) == attrRes) {
+            return (ssize_t)i;
+        }
+    }
+    return -1;
+}
+
+String8 getAttribute(const ResXMLTree& tree, const char* ns,
+        const char* attr, String8* outError) {
+    ssize_t idx = tree.indexOfAttribute(ns, attr);
+    if (idx < 0) {
+        return String8();
+    }
+    return getStringAttributeAtIndex(tree, idx, outError);
+}
+
+String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError) {
+    ssize_t idx = indexOfAttribute(tree, attrRes);
+    if (idx < 0) {
+        return String8();
+    }
+    return getStringAttributeAtIndex(tree, idx, outError);
+}
+
+String8 getResolvedAttribute(const ResTable& resTable, const ResXMLTree& tree,
+        uint32_t attrRes, String8* outError) {
+    ssize_t idx = indexOfAttribute(tree, attrRes);
+    if (idx < 0) {
+        return String8();
+    }
+    Res_value value;
+    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
+        if (value.dataType == Res_value::TYPE_STRING) {
+            size_t len;
+            const uint16_t* str = tree.getAttributeStringValue(idx, &len);
+            return str ? String8(str, len) : String8();
+        }
+        resTable.resolveReference(&value, 0);
+        if (value.dataType != Res_value::TYPE_STRING) {
+            if (outError != NULL) {
+                *outError = "attribute is not a string value";
+            }
+            return String8();
+        }
+    }
+    size_t len;
+    const Res_value* value2 = &value;
+    const char16_t* str = resTable.valueToString(value2, 0, NULL, &len);
+    return str ? String8(str, len) : String8();
+}
+
+int32_t getIntegerAttribute(const ResXMLTree& tree, const char* ns,
+        const char* attr, int32_t defValue, String8* outError) {
+    ssize_t idx = tree.indexOfAttribute(ns, attr);
+    if (idx < 0) {
+        return defValue;
+    }
+    return getIntegerAttributeAtIndex(tree, idx, defValue, outError);
+}
+
+int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes, int32_t defValue,
+        String8* outError) {
+    ssize_t idx = indexOfAttribute(tree, attrRes);
+    if (idx < 0) {
+        return defValue;
+    }
+    return getIntegerAttributeAtIndex(tree, idx, defValue, outError);
+}
+
+int32_t getResolvedIntegerAttribute(const ResTable& resTable, const ResXMLTree& tree,
+        uint32_t attrRes, int32_t defValue, String8* outError) {
+    ssize_t idx = indexOfAttribute(tree, attrRes);
+    if (idx < 0) {
+        return defValue;
+    }
+    Res_value value;
+    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
+        if (value.dataType == Res_value::TYPE_REFERENCE) {
+            resTable.resolveReference(&value, 0);
+        }
+        if (value.dataType < Res_value::TYPE_FIRST_INT
+                || value.dataType > Res_value::TYPE_LAST_INT) {
+            if (outError != NULL) {
+                *outError = "attribute is not an integer value";
+            }
+            return defValue;
+        }
+    }
+    return value.data;
+}
+
+void getResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
+        uint32_t attrRes, Res_value* outValue, String8* outError) {
+    ssize_t idx = indexOfAttribute(tree, attrRes);
+    if (idx < 0) {
+        if (outError != NULL) {
+            *outError = "attribute could not be found";
+        }
+        return;
+    }
+    if (tree.getAttributeValue(idx, outValue) != NO_ERROR) {
+        if (outValue->dataType == Res_value::TYPE_REFERENCE) {
+            resTable.resolveReference(outValue, 0);
+        }
+        // The attribute was found and was resolved if need be.
+        return;
+    }
+    if (outError != NULL) {
+        *outError = "error getting resolved resource attribute";
+    }
+}
+
+} // namespace AaptXml
diff --git a/tools/aapt/AaptXml.h b/tools/aapt/AaptXml.h
new file mode 100644
index 0000000..16977f3
--- /dev/null
+++ b/tools/aapt/AaptXml.h
@@ -0,0 +1,123 @@
+/*
+ * 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 __AAPT_XML_H
+#define __AAPT_XML_H
+
+#include <androidfw/ResourceTypes.h>
+#include <utils/String8.h>
+
+/**
+ * Utility methods for dealing with ResXMLTree.
+ */
+namespace AaptXml {
+
+/**
+ * Returns the index of the attribute, or < 0 if it was not found.
+ */
+ssize_t indexOfAttribute(const android::ResXMLTree& tree, uint32_t attrRes);
+
+/**
+ * Returns the string value for the specified attribute.
+ * The string must be present in the ResXMLTree's string pool (inline in the XML).
+ */
+android::String8 getAttribute(const android::ResXMLTree& tree, const char* ns,
+        const char* attr, android::String8* outError = NULL);
+
+/**
+ * Returns the string value for the specified attribute, or an empty string
+ * if the attribute does not exist.
+ * The string must be present in the ResXMLTree's string pool (inline in the XML).
+ */
+android::String8 getAttribute(const android::ResXMLTree& tree, uint32_t attrRes,
+        android::String8* outError = NULL);
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer must be declared inline in the XML.
+ */
+int32_t getIntegerAttribute(const android::ResXMLTree& tree, const char* ns,
+        const char* attr, int32_t defValue = -1, android::String8* outError = NULL);
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer must be declared inline in the XML.
+ */
+inline int32_t getIntegerAttribute(const android::ResXMLTree& tree, const char* ns,
+        const char* attr, android::String8* outError) {
+    return getIntegerAttribute(tree, ns, attr, -1, outError);
+}
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer must be declared inline in the XML.
+ */
+int32_t getIntegerAttribute(const android::ResXMLTree& tree, uint32_t attrRes,
+        int32_t defValue = -1, android::String8* outError = NULL);
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer must be declared inline in the XML.
+ */
+inline int32_t getIntegerAttribute(const android::ResXMLTree& tree, uint32_t attrRes,
+        android::String8* outError) {
+    return getIntegerAttribute(tree, attrRes, -1, outError);
+}
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer may be a resource in the supplied ResTable.
+ */
+int32_t getResolvedIntegerAttribute(const android::ResTable& resTable,
+        const android::ResXMLTree& tree, uint32_t attrRes, int32_t defValue = -1,
+        android::String8* outError = NULL);
+
+/**
+ * Returns the integer value for the specified attribute, or the default value
+ * if the attribute does not exist.
+ * The integer may be a resource in the supplied ResTable.
+ */
+inline int32_t getResolvedIntegerAttribute(const android::ResTable& resTable,
+        const android::ResXMLTree& tree, uint32_t attrRes,
+        android::String8* outError) {
+    return getResolvedIntegerAttribute(resTable, tree, attrRes, -1, outError);
+}
+
+/**
+ * Returns the string value for the specified attribute, or an empty string
+ * if the attribute does not exist.
+ * The string may be a resource in the supplied ResTable.
+ */
+android::String8 getResolvedAttribute(const android::ResTable& resTable,
+        const android::ResXMLTree& tree, uint32_t attrRes,
+        android::String8* outError = NULL);
+
+/**
+ * Returns the resource for the specified attribute in the outValue parameter.
+ * The resource may be a resource in the supplied ResTable.
+ */
+void getResolvedResourceAttribute(const android::ResTable& resTable,
+        const android::ResXMLTree& tree, uint32_t attrRes, android::Res_value* outValue,
+        android::String8* outError = NULL);
+
+} // namespace AaptXml
+
+#endif // __AAPT_XML_H
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 4ce5045..2cbabe1 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -28,6 +28,7 @@
     AaptAssets.cpp \
     AaptConfig.cpp \
     AaptUtil.cpp \
+    AaptXml.cpp \
     ApkBuilder.cpp \
     Command.cpp \
     CrunchCache.cpp \
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index af49461..9bed899 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -130,6 +130,10 @@
     void setErrorOnFailedInsert(bool val) { mErrorOnFailedInsert = val; }
     bool getErrorOnMissingConfigEntry() { return mErrorOnMissingConfigEntry; }
     void setErrorOnMissingConfigEntry(bool val) { mErrorOnMissingConfigEntry = val; }
+    const android::String8& getPlatformBuildVersionCode() { return mPlatformVersionCode; }
+    void setPlatformBuildVersionCode(const android::String8& code) { mPlatformVersionCode = code; }
+    const android::String8& getPlatformBuildVersionName() { return mPlatformVersionName; }
+    void setPlatformBuildVersionName(const android::String8& name) { mPlatformVersionName = name; }
 
     bool getUTF16StringsOption() {
         return mWantUTF16 || !isMinSdkAtLeast(SDK_FROYO);
@@ -323,6 +327,8 @@
     const char* mSingleCrunchInputFile;
     const char* mSingleCrunchOutputFile;
     bool        mBuildSharedLibrary;
+    android::String8 mPlatformVersionCode;
+    android::String8 mPlatformVersionName;
 
     /* file specification */
     int         mArgc;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index a0f0a08..27e60f3 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -3,6 +3,7 @@
 //
 // Android Asset Packaging Tool main entry point.
 //
+#include "AaptXml.h"
 #include "ApkBuilder.h"
 #include "Bundle.h"
 #include "Images.h"
@@ -241,162 +242,17 @@
     return result;
 }
 
-static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
-{
-    size_t N = tree.getAttributeCount();
-    for (size_t i=0; i<N; i++) {
-        if (tree.getAttributeNameResID(i) == attrRes) {
-            return (ssize_t)i;
-        }
-    }
-    return -1;
-}
-
-String8 getAttribute(const ResXMLTree& tree, const char* ns,
-                            const char* attr, String8* outError)
-{
-    ssize_t idx = tree.indexOfAttribute(ns, attr);
-    if (idx < 0) {
-        return String8();
-    }
-    Res_value value;
-    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
-        if (value.dataType != Res_value::TYPE_STRING) {
-            if (outError != NULL) {
-                *outError = "attribute is not a string value";
-            }
-            return String8();
-        }
-    }
-    size_t len;
-    const uint16_t* str = tree.getAttributeStringValue(idx, &len);
-    return str ? String8(str, len) : String8();
-}
-
-static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError)
-{
-    ssize_t idx = indexOfAttribute(tree, attrRes);
-    if (idx < 0) {
-        return String8();
-    }
-    Res_value value;
-    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
-        if (value.dataType != Res_value::TYPE_STRING) {
-            if (outError != NULL) {
-                *outError = "attribute is not a string value";
-            }
-            return String8();
-        }
-    }
-    size_t len;
-    const uint16_t* str = tree.getAttributeStringValue(idx, &len);
-    return str ? String8(str, len) : String8();
-}
-
-static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
-        String8* outError, int32_t defValue = -1)
-{
-    ssize_t idx = indexOfAttribute(tree, attrRes);
-    if (idx < 0) {
-        return defValue;
-    }
-    Res_value value;
-    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
-        if (value.dataType < Res_value::TYPE_FIRST_INT
-                || value.dataType > Res_value::TYPE_LAST_INT) {
-            if (outError != NULL) {
-                *outError = "attribute is not an integer value";
-            }
-            return defValue;
-        }
-    }
-    return value.data;
-}
-
-static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
-        uint32_t attrRes, String8* outError, int32_t defValue = -1)
-{
-    ssize_t idx = indexOfAttribute(tree, attrRes);
-    if (idx < 0) {
-        return defValue;
-    }
-    Res_value value;
-    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
-        if (value.dataType == Res_value::TYPE_REFERENCE) {
-            resTable->resolveReference(&value, 0);
-        }
-        if (value.dataType < Res_value::TYPE_FIRST_INT
-                || value.dataType > Res_value::TYPE_LAST_INT) {
-            if (outError != NULL) {
-                *outError = "attribute is not an integer value";
-            }
-            return defValue;
-        }
-    }
-    return value.data;
-}
-
-static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
-        uint32_t attrRes, String8* outError)
-{
-    ssize_t idx = indexOfAttribute(tree, attrRes);
-    if (idx < 0) {
-        return String8();
-    }
-    Res_value value;
-    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
-        if (value.dataType == Res_value::TYPE_STRING) {
-            size_t len;
-            const uint16_t* str = tree.getAttributeStringValue(idx, &len);
-            return str ? String8(str, len) : String8();
-        }
-        resTable->resolveReference(&value, 0);
-        if (value.dataType != Res_value::TYPE_STRING) {
-            if (outError != NULL) {
-                *outError = "attribute is not a string value";
-            }
-            return String8();
-        }
-    }
-    size_t len;
-    const Res_value* value2 = &value;
-    const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len);
-    return str ? String8(str, len) : String8();
-}
-
-static void getResolvedResourceAttribute(Res_value* value, const ResTable* resTable,
-        const ResXMLTree& tree, uint32_t attrRes, String8* outError)
-{
-    ssize_t idx = indexOfAttribute(tree, attrRes);
-    if (idx < 0) {
-        if (outError != NULL) {
-            *outError = "attribute could not be found";
-        }
-        return;
-    }
-    if (tree.getAttributeValue(idx, value) != NO_ERROR) {
-        if (value->dataType == Res_value::TYPE_REFERENCE) {
-            resTable->resolveReference(value, 0);
-        }
-        // The attribute was found and was resolved if need be.
-        return;
-    }
-    if (outError != NULL) {
-        *outError = "error getting resolved resource attribute";
-    }
-}
-
-static void printResolvedResourceAttribute(const ResTable* resTable, const ResXMLTree& tree,
+static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree,
         uint32_t attrRes, String8 attrLabel, String8* outError)
 {
     Res_value value;
-    getResolvedResourceAttribute(&value, resTable, tree, attrRes, outError);
+    AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError);
     if (*outError != "") {
         *outError = "error print resolved resource attribute";
         return;
     }
     if (value.dataType == Res_value::TYPE_STRING) {
-        String8 result = getResolvedAttribute(resTable, tree, attrRes, outError);
+        String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
         printf("%s='%s'", attrLabel.string(),
                 ResTable::normalizeForOutput(result.string()).string());
     } else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
@@ -488,10 +344,10 @@
         }
         String8 tag(ctag16);
         if (tag == "screen") {
-            int32_t screenSize = getIntegerAttribute(tree,
-                    SCREEN_SIZE_ATTR, NULL, -1);
-            int32_t screenDensity = getIntegerAttribute(tree,
-                    SCREEN_DENSITY_ATTR, NULL, -1);
+            int32_t screenSize = AaptXml::getIntegerAttribute(tree,
+                    SCREEN_SIZE_ATTR);
+            int32_t screenDensity = AaptXml::getIntegerAttribute(tree,
+                    SCREEN_DENSITY_ATTR);
             if (screenSize > 0 && screenDensity > 0) {
                 if (!first) {
                     printf(",");
@@ -577,7 +433,7 @@
                 }
             } else if (depth == 2 && withinApduService) {
                 if (tag == "aid-group") {
-                    String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
+                    String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
                     if (error != "") {
                         if (outError != NULL) *outError = error;
                         return Vector<String8>();
@@ -876,11 +732,11 @@
                         fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
                         goto bail;
                     }
-                    String8 pkg = getAttribute(tree, NULL, "package", NULL);
+                    String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
                     printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
                 } else if (depth == 2 && tag == "permission") {
                     String8 error;
-                    String8 name = getAttribute(tree, NAME_ATTR, &error);
+                    String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                     if (error != "") {
                         fprintf(stderr, "ERROR: %s\n", error.string());
                         goto bail;
@@ -889,14 +745,14 @@
                             ResTable::normalizeForOutput(name.string()).string());
                 } else if (depth == 2 && tag == "uses-permission") {
                     String8 error;
-                    String8 name = getAttribute(tree, NAME_ATTR, &error);
+                    String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                     if (error != "") {
                         fprintf(stderr, "ERROR: %s\n", error.string());
                         goto bail;
                     }
                     printUsesPermission(name,
-                            getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
-                            getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
+                            AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
+                            AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
                 }
             }
         } else if (strcmp("badging", option) == 0) {
@@ -1151,12 +1007,14 @@
                         fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
                         goto bail;
                     }
-                    pkg = getAttribute(tree, NULL, "package", NULL);
+                    pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
                     printf("package: name='%s' ",
                             ResTable::normalizeForOutput(pkg.string()).string());
-                    int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
+                    int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
+                            &error);
                     if (error != "") {
-                        fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string());
+                        fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
+                                error.string());
                         goto bail;
                     }
                     if (versionCode > 0) {
@@ -1164,23 +1022,29 @@
                     } else {
                         printf("versionCode='' ");
                     }
-                    String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error);
+                    String8 versionName = AaptXml::getResolvedAttribute(res, tree,
+                            VERSION_NAME_ATTR, &error);
                     if (error != "") {
-                        fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string());
+                        fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
+                                error.string());
                         goto bail;
                     }
                     printf("versionName='%s'",
                             ResTable::normalizeForOutput(versionName.string()).string());
 
-                    String8 splitName = getAttribute(tree, NULL, "split", NULL);
+                    String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
                     if (!splitName.isEmpty()) {
                         printf(" split='%s'", ResTable::normalizeForOutput(
                                     splitName.string()).string());
                     }
+
+                    String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
+                            "platformBuildVersionName");
+                    printf(" platformBuildVersionName='%s'", platformVersionName.string());
                     printf("\n");
 
-                    int32_t installLocation = getResolvedIntegerAttribute(&res, tree,
-                            INSTALL_LOCATION_ATTR, &error, -1);
+                    int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
+                            INSTALL_LOCATION_ATTR, &error);
                     if (error != "") {
                         fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
                                 error.string());
@@ -1215,7 +1079,8 @@
                         for (size_t i=0; i<NL; i++) {
                             const char* localeStr =  locales[i].string();
                             assets.setLocale(localeStr != NULL ? localeStr : "");
-                            String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+                            String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
+                                    &error);
                             if (llabel != "") {
                                 if (localeStr == NULL || strlen(localeStr) == 0) {
                                     label = llabel;
@@ -1236,7 +1101,8 @@
                         for (size_t i=0; i<ND; i++) {
                             tmpConfig.density = densities[i];
                             assets.setConfiguration(tmpConfig);
-                            String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+                            String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
+                                    &error);
                             if (icon != "") {
                                 printf("application-icon-%d:'%s'\n", densities[i],
                                         ResTable::normalizeForOutput(icon.string()).string());
@@ -1244,14 +1110,17 @@
                         }
                         assets.setConfiguration(config);
 
-                        String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+                        String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
                         if (error != "") {
-                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
+                            fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
+                                    error.string());
                             goto bail;
                         }
-                        int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
+                        int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
+                                &error);
                         if (error != "") {
-                            fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
+                            fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n",
+                                    error.string());
                             goto bail;
                         }
                         printf("application: label='%s' ",
@@ -1261,9 +1130,11 @@
                             printf("testOnly='%d'\n", testOnly);
                         }
 
-                        int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0);
+                        int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree,
+                                DEBUGGABLE_ATTR, 0, &error);
                         if (error != "") {
-                            fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string());
+                            fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n",
+                                    error.string());
                             goto bail;
                         }
                         if (debuggable != 0) {
@@ -1284,10 +1155,11 @@
                             }
                         }
                     } else if (tag == "uses-sdk") {
-                        int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
+                        int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
                         if (error != "") {
                             error = "";
-                            String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error);
+                            String8 name = AaptXml::getResolvedAttribute(res, tree,
+                                    MIN_SDK_VERSION_ATTR, &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
                                         error.string());
@@ -1300,14 +1172,15 @@
                             targetSdk = code;
                             printf("sdkVersion:'%d'\n", code);
                         }
-                        code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1);
+                        code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR);
                         if (code != -1) {
                             printf("maxSdkVersion:'%d'\n", code);
                         }
-                        code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
+                        code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error);
                         if (error != "") {
                             error = "";
-                            String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error);
+                            String8 name = AaptXml::getResolvedAttribute(res, tree,
+                                    TARGET_SDK_VERSION_ATTR, &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
                                         error.string());
@@ -1323,16 +1196,16 @@
                             printf("targetSdkVersion:'%d'\n", code);
                         }
                     } else if (tag == "uses-configuration") {
-                        int32_t reqTouchScreen = getIntegerAttribute(tree,
-                                REQ_TOUCH_SCREEN_ATTR, NULL, 0);
-                        int32_t reqKeyboardType = getIntegerAttribute(tree,
-                                REQ_KEYBOARD_TYPE_ATTR, NULL, 0);
-                        int32_t reqHardKeyboard = getIntegerAttribute(tree,
-                                REQ_HARD_KEYBOARD_ATTR, NULL, 0);
-                        int32_t reqNavigation = getIntegerAttribute(tree,
-                                REQ_NAVIGATION_ATTR, NULL, 0);
-                        int32_t reqFiveWayNav = getIntegerAttribute(tree,
-                                REQ_FIVE_WAY_NAV_ATTR, NULL, 0);
+                        int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree,
+                                REQ_TOUCH_SCREEN_ATTR, 0);
+                        int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree,
+                                REQ_KEYBOARD_TYPE_ATTR, 0);
+                        int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree,
+                                REQ_HARD_KEYBOARD_ATTR, 0);
+                        int32_t reqNavigation = AaptXml::getIntegerAttribute(tree,
+                                REQ_NAVIGATION_ATTR, 0);
+                        int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree,
+                                REQ_FIVE_WAY_NAV_ATTR, 0);
                         printf("uses-configuration:");
                         if (reqTouchScreen != 0) {
                             printf(" reqTouchScreen='%d'", reqTouchScreen);
@@ -1353,26 +1226,26 @@
                     } else if (tag == "supports-input") {
                         withinSupportsInput = true;
                     } else if (tag == "supports-screens") {
-                        smallScreen = getIntegerAttribute(tree,
-                                SMALL_SCREEN_ATTR, NULL, 1);
-                        normalScreen = getIntegerAttribute(tree,
-                                NORMAL_SCREEN_ATTR, NULL, 1);
-                        largeScreen = getIntegerAttribute(tree,
-                                LARGE_SCREEN_ATTR, NULL, 1);
-                        xlargeScreen = getIntegerAttribute(tree,
-                                XLARGE_SCREEN_ATTR, NULL, 1);
-                        anyDensity = getIntegerAttribute(tree,
-                                ANY_DENSITY_ATTR, NULL, 1);
-                        requiresSmallestWidthDp = getIntegerAttribute(tree,
-                                REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
-                        compatibleWidthLimitDp = getIntegerAttribute(tree,
-                                COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
-                        largestWidthLimitDp = getIntegerAttribute(tree,
-                                LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
+                        smallScreen = AaptXml::getIntegerAttribute(tree,
+                                SMALL_SCREEN_ATTR, 1);
+                        normalScreen = AaptXml::getIntegerAttribute(tree,
+                                NORMAL_SCREEN_ATTR, 1);
+                        largeScreen = AaptXml::getIntegerAttribute(tree,
+                                LARGE_SCREEN_ATTR, 1);
+                        xlargeScreen = AaptXml::getIntegerAttribute(tree,
+                                XLARGE_SCREEN_ATTR, 1);
+                        anyDensity = AaptXml::getIntegerAttribute(tree,
+                                ANY_DENSITY_ATTR, 1);
+                        requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree,
+                                REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0);
+                        compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree,
+                                COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0);
+                        largestWidthLimitDp = AaptXml::getIntegerAttribute(tree,
+                                LARGEST_WIDTH_LIMIT_DP_ATTR, 0);
                     } else if (tag == "feature-group") {
                         withinFeatureGroup = true;
                         FeatureGroup group;
-                        group.label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+                        group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
                         if (error != "") {
                             fprintf(stderr, "ERROR getting 'android:label' attribute:"
                                     " %s\n", error.string());
@@ -1381,17 +1254,17 @@
                         featureGroups.add(group);
 
                     } else if (tag == "uses-feature") {
-                        String8 name = getAttribute(tree, NAME_ATTR, &error);
+                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
-                            int req = getIntegerAttribute(tree,
-                                    REQUIRED_ATTR, NULL, 1);
+                            int req = AaptXml::getIntegerAttribute(tree,
+                                    REQUIRED_ATTR, 1);
 
                             commonFeatures.features.add(name, req);
                             if (req) {
                                 addParentFeatures(&commonFeatures, name);
                             }
                         } else {
-                            int vers = getIntegerAttribute(tree,
+                            int vers = AaptXml::getIntegerAttribute(tree,
                                     GL_ES_VERSION_ATTR, &error);
                             if (error == "") {
                                 if (vers > commonFeatures.openGLESVersion) {
@@ -1400,7 +1273,7 @@
                             }
                         }
                     } else if (tag == "uses-permission") {
-                        String8 name = getAttribute(tree, NAME_ATTR, &error);
+                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
                             if (name == "android.permission.CAMERA") {
                                 addImpliedFeature(&impliedFeatures, "android.hardware.camera",
@@ -1478,15 +1351,15 @@
                             }
 
                             printUsesPermission(name,
-                                    getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0,
-                                    getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1));
+                                    AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
+                                    AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
                        } else {
                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
                                     error.string());
                             goto bail;
                         }
                     } else if (tag == "uses-package") {
-                        String8 name = getAttribute(tree, NAME_ATTR, &error);
+                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
                             printf("uses-package:'%s'\n",
                                     ResTable::normalizeForOutput(name.string()).string());
@@ -1496,7 +1369,7 @@
                                 goto bail;
                         }
                     } else if (tag == "original-package") {
-                        String8 name = getAttribute(tree, NAME_ATTR, &error);
+                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
                             printf("original-package:'%s'\n",
                                     ResTable::normalizeForOutput(name.string()).string());
@@ -1506,7 +1379,7 @@
                                 goto bail;
                         }
                     } else if (tag == "supports-gl-texture") {
-                        String8 name = getAttribute(tree, NAME_ATTR, &error);
+                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
                             printf("supports-gl-texture:'%s'\n",
                                     ResTable::normalizeForOutput(name.string()).string());
@@ -1524,9 +1397,9 @@
                         }
                         depth--;
                     } else if (tag == "package-verifier") {
-                        String8 name = getAttribute(tree, NAME_ATTR, &error);
+                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
-                            String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
+                            String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error);
                             if (publicKey != "" && error == "") {
                                 printf("package-verifier: name='%s' publicKey='%s'\n",
                                         ResTable::normalizeForOutput(name.string()).string(),
@@ -1553,35 +1426,38 @@
                     if (withinApplication) {
                         if(tag == "activity") {
                             withinActivity = true;
-                            activityName = getAttribute(tree, NAME_ATTR, &error);
+                            activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
                                         error.string());
                                 goto bail;
                             }
 
-                            activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+                            activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
+                                    &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n",
                                         error.string());
                                 goto bail;
                             }
 
-                            activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+                            activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR,
+                                    &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n",
                                         error.string());
                                 goto bail;
                             }
 
-                            activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error);
+                            activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
+                                    &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
                                         error.string());
                                 goto bail;
                             }
 
-                            int32_t orien = getResolvedIntegerAttribute(&res, tree,
+                            int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree,
                                     SCREEN_ORIENTATION_ATTR, &error);
                             if (error == "") {
                                 if (orien == 0 || orien == 6 || orien == 8) {
@@ -1595,21 +1471,21 @@
                                 }
                             }
                         } else if (tag == "uses-library") {
-                            String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
+                            String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                             if (error != "") {
                                 fprintf(stderr,
                                         "ERROR getting 'android:name' attribute for uses-library"
                                         " %s\n", error.string());
                                 goto bail;
                             }
-                            int req = getIntegerAttribute(tree,
-                                    REQUIRED_ATTR, NULL, 1);
+                            int req = AaptXml::getIntegerAttribute(tree,
+                                    REQUIRED_ATTR, 1);
                             printf("uses-library%s:'%s'\n",
                                     req ? "" : "-not-required", ResTable::normalizeForOutput(
                                             libraryName.string()).string());
                         } else if (tag == "receiver") {
                             withinReceiver = true;
-                            receiverName = getAttribute(tree, NAME_ATTR, &error);
+                            receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
 
                             if (error != "") {
                                 fprintf(stderr,
@@ -1618,7 +1494,8 @@
                                 goto bail;
                             }
 
-                            String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
+                            String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
+                                    &error);
                             if (error == "") {
                                 if (permission == "android.permission.BIND_DEVICE_ADMIN") {
                                     hasBindDeviceAdminPermission = true;
@@ -1629,7 +1506,7 @@
                             }
                         } else if (tag == "service") {
                             withinService = true;
-                            serviceName = getAttribute(tree, NAME_ATTR, &error);
+                            serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
 
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:name' attribute for "
@@ -1637,7 +1514,8 @@
                                 goto bail;
                             }
 
-                            String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
+                            String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
+                                    &error);
                             if (error == "") {
                                 if (permission == "android.permission.BIND_INPUT_METHOD") {
                                     hasBindInputMethodPermission = true;
@@ -1659,22 +1537,24 @@
                         } else if (tag == "provider") {
                             withinProvider = true;
 
-                            bool exported = getResolvedIntegerAttribute(&res, tree, EXPORTED_ATTR, &error);
+                            bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
+                                    EXPORTED_ATTR, &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
                                         " %s\n", error.string());
                                 goto bail;
                             }
 
-                            bool grantUriPermissions = getResolvedIntegerAttribute(&res, tree,
-                                    GRANT_URI_PERMISSIONS_ATTR, &error);
+                            bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
+                                    res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
                                         " %s\n", error.string());
                                 goto bail;
                             }
 
-                            String8 permission = getResolvedAttribute(&res, tree, PERMISSION_ATTR, &error);
+                            String8 permission = AaptXml::getResolvedAttribute(res, tree,
+                                    PERMISSION_ATTR, &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
                                         " %s\n", error.string());
@@ -1685,7 +1565,8 @@
                                 permission == "android.permission.MANAGE_DOCUMENTS";
 
                         } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
-                            String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
+                            String8 metaDataName = AaptXml::getResolvedAttribute(res, tree,
+                                    NAME_ATTR, &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:name' attribute for "
                                         "meta-data:%s\n", error.string());
@@ -1693,12 +1574,12 @@
                             }
                             printf("meta-data: name='%s' ",
                                     ResTable::normalizeForOutput(metaDataName.string()).string());
-                            printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"),
+                            printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
                                     &error);
                             if (error != "") {
                                 // Try looking for a RESOURCE_ATTR
                                 error = "";
-                                printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR,
+                                printResolvedResourceAttribute(res, tree, RESOURCE_ATTR,
                                         String8("resource"), &error);
                                 if (error != "") {
                                     fprintf(stderr, "ERROR getting 'android:value' or "
@@ -1709,7 +1590,7 @@
                             }
                             printf("\n");
                         } else if (withinSupportsInput && tag == "input-type") {
-                            String8 name = getAttribute(tree, NAME_ATTR, &error);
+                            String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                             if (name != "" && error == "") {
                                 supportedInput.add(name);
                             } else {
@@ -1721,12 +1602,13 @@
                     } else if (withinFeatureGroup && tag == "uses-feature") {
                         FeatureGroup& top = featureGroups.editTop();
 
-                        String8 name = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
+                        String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
                             top.features.add(name, true);
                             addParentFeatures(&top, name);
                         } else {
-                            int vers = getIntegerAttribute(tree, GL_ES_VERSION_ATTR, &error);
+                            int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
+                                    &error);
                             if (error == "") {
                                 if (vers > top.openGLESVersion) {
                                     top.openGLESVersion = vers;
@@ -1754,7 +1636,7 @@
                         actCameraSecure = false;
                         catLauncher = false;
                     } else if (withinService && tag == "meta-data") {
-                        String8 name = getAttribute(tree, NAME_ATTR, &error);
+                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (error != "") {
                             fprintf(stderr, "ERROR getting 'android:name' attribute for"
                                     " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
@@ -1768,7 +1650,8 @@
                                 offHost = false;
                             }
 
-                            String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
+                            String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
+                                    RESOURCE_ATTR, &error);
                             if (error != "") {
                                 fprintf(stderr, "ERROR getting 'android:resource' attribute for"
                                         " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
@@ -1797,7 +1680,7 @@
                 } else if ((depth == 5) && withinIntentFilter) {
                     String8 action;
                     if (tag == "action") {
-                        action = getAttribute(tree, NAME_ATTR, &error);
+                        action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (error != "") {
                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
                                     error.string());
@@ -1849,7 +1732,7 @@
                     }
 
                     if (tag == "category") {
-                        String8 category = getAttribute(tree, NAME_ATTR, &error);
+                        String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (error != "") {
                             fprintf(stderr, "ERROR getting 'name' attribute: %s\n",
                                     error.string());
diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h
index dd40b20..f24a023b 100644
--- a/tools/aapt/Main.h
+++ b/tools/aapt/Main.h
@@ -60,9 +60,6 @@
 
 int dumpResources(Bundle* bundle);
 
-String8 getAttribute(const ResXMLTree& tree, const char* ns,
-                            const char* attr, String8* outError);
-
 status_t writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets,
                                 FILE* fp, bool includeRaw);
 #endif // __MAIN_H
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 7979a1d..5deeca2 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -4,6 +4,7 @@
 // Build resource files from raw assets.
 //
 #include "AaptAssets.h"
+#include "AaptXml.h"
 #include "CacheUpdater.h"
 #include "CrunchCache.h"
 #include "FileFinder.h"
@@ -805,6 +806,20 @@
         }
     }
 
+    if (bundle->getPlatformBuildVersionCode() != "") {
+        if (!addTagAttribute(root, "", "platformBuildVersionCode",
+                    bundle->getPlatformBuildVersionCode(), errorOnFailedInsert, true)) {
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    if (bundle->getPlatformBuildVersionName() != "") {
+        if (!addTagAttribute(root, "", "platformBuildVersionName",
+                    bundle->getPlatformBuildVersionName(), errorOnFailedInsert, true)) {
+            return UNKNOWN_ERROR;
+        }
+    }
+
     if (bundle->getDebugMode()) {
         sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
         if (application != NULL) {
@@ -881,6 +896,106 @@
     return NO_ERROR;
 }
 
+static int32_t getPlatformAssetCookie(const AssetManager& assets) {
+    // Find the system package (0x01). AAPT always generates attributes
+    // with the type 0x01, so we're looking for the first attribute
+    // resource in the system package.
+    const ResTable& table = assets.getResources(true);
+    Res_value val;
+    ssize_t idx = table.getResource(0x01010000, &val, true);
+    if (idx != NO_ERROR) {
+        // Try as a bag.
+        const ResTable::bag_entry* entry;
+        ssize_t cnt = table.lockBag(0x01010000, &entry);
+        if (cnt >= 0) {
+            idx = entry->stringBlock;
+        }
+        table.unlockBag(entry);
+    }
+
+    if (idx < 0) {
+        return 0;
+    }
+    return table.getTableCookie(idx);
+}
+
+enum {
+    VERSION_CODE_ATTR = 0x0101021b,
+    VERSION_NAME_ATTR = 0x0101021c,
+};
+
+static ssize_t extractPlatformBuildVersion(ResXMLTree& tree, Bundle* bundle) {
+    size_t len;
+    ResXMLTree::event_code_t code;
+    while ((code = tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+        if (code != ResXMLTree::START_TAG) {
+            continue;
+        }
+
+        const char16_t* ctag16 = tree.getElementName(&len);
+        if (ctag16 == NULL) {
+            fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
+            return UNKNOWN_ERROR;
+        }
+
+        String8 tag(ctag16, len);
+        if (tag != "manifest") {
+            continue;
+        }
+
+        String8 error;
+        int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR, &error);
+        if (error != "") {
+            fprintf(stderr, "ERROR: failed to get platform version code\n");
+            return UNKNOWN_ERROR;
+        }
+
+        if (versionCode >= 0 && bundle->getPlatformBuildVersionCode() == "") {
+            bundle->setPlatformBuildVersionCode(String8::format("%d", versionCode));
+        }
+
+        String8 versionName = AaptXml::getAttribute(tree, VERSION_NAME_ATTR, &error);
+        if (error != "") {
+            fprintf(stderr, "ERROR: failed to get platform version name\n");
+            return UNKNOWN_ERROR;
+        }
+
+        if (versionName != "" && bundle->getPlatformBuildVersionName() == "") {
+            bundle->setPlatformBuildVersionName(versionName);
+        }
+        return NO_ERROR;
+    }
+
+    fprintf(stderr, "ERROR: no <manifest> tag found in platform AndroidManifest.xml\n");
+    return UNKNOWN_ERROR;
+}
+
+static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) {
+    int32_t cookie = getPlatformAssetCookie(assets);
+    if (cookie == 0) {
+        fprintf(stderr, "ERROR: Platform package not found\n");
+        return UNKNOWN_ERROR;
+    }
+
+    ResXMLTree tree;
+    Asset* asset = assets.openNonAsset(cookie, "AndroidManifest.xml", Asset::ACCESS_STREAMING);
+    if (asset == NULL) {
+        fprintf(stderr, "ERROR: Platform AndroidManifest.xml not found\n");
+        return UNKNOWN_ERROR;
+    }
+
+    ssize_t result = NO_ERROR;
+    if (tree.setTo(asset->getBuffer(true), asset->getLength()) != NO_ERROR) {
+        fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n");
+        result = UNKNOWN_ERROR;
+    } else {
+        result = extractPlatformBuildVersion(tree, bundle);
+    }
+
+    delete asset;
+    return result;
+}
+
 #define ASSIGN_IT(n) \
         do { \
             ssize_t index = resources->indexOfKey(String8(#n)); \
@@ -1356,6 +1471,17 @@
         return UNKNOWN_ERROR;
     }
 
+    // If we're not overriding the platform build versions,
+    // extract them from the platform APK.
+    if (packageType != ResourceTable::System &&
+            (bundle->getPlatformBuildVersionCode() == "" ||
+            bundle->getPlatformBuildVersionName() == "")) {
+        err = extractPlatformBuildVersion(assets->getAssetManager(), bundle);
+        if (err != NO_ERROR) {
+            return UNKNOWN_ERROR;
+        }
+    }
+
     const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0));
     String8 manifestPath(manifestFile->getPrintableSource());
 
@@ -2636,13 +2762,14 @@
                 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
                 return -1;
             }
-            pkg = getAttribute(tree, NULL, "package", NULL);
+            pkg = AaptXml::getAttribute(tree, NULL, "package");
         } else if (depth == 2) {
             if (tag == "application") {
                 inApplication = true;
                 keepTag = true;
 
-                String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android",
+                String8 agent = AaptXml::getAttribute(tree,
+                        "http://schemas.android.com/apk/res/android",
                         "backupAgent", &error);
                 if (agent.length() > 0) {
                     addProguardKeepRule(keep, agent, pkg.string(),
@@ -2658,8 +2785,8 @@
             }
         }
         if (keepTag) {
-            String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android",
-                    "name", &error);
+            String8 name = AaptXml::getAttribute(tree,
+                    "http://schemas.android.com/apk/res/android", "name", &error);
             if (error != "") {
                 fprintf(stderr, "ERROR: %s\n", error.string());
                 return -1;