Merge "Copy certain settings to the managed profile" into lmp-dev
diff --git a/CleanSpec.mk b/CleanSpec.mk
index ae2f3ad..c7cf940 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -218,6 +218,9 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/services.core_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/services_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/services.core_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/services_intermediates)
 
 # ******************************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
diff --git a/api/current.txt b/api/current.txt
index b3d4e60..c0bfd83 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -464,7 +464,6 @@
     field public static final int contentInsetLeft = 16843861; // 0x1010455
     field public static final int contentInsetRight = 16843862; // 0x1010456
     field public static final int contentInsetStart = 16843859; // 0x1010453
-    field public static final int contentRatingSystemXml = 16843955; // 0x10104b3
     field public static final int controlX1 = 16843798; // 0x1010416
     field public static final int controlX2 = 16843800; // 0x1010418
     field public static final int controlY1 = 16843799; // 0x1010417
@@ -1355,6 +1354,7 @@
     field public static final int trimPathEnd = 16843811; // 0x1010423
     field public static final int trimPathOffset = 16843812; // 0x1010424
     field public static final int trimPathStart = 16843810; // 0x1010422
+    field public static final int tvContentRatingDescription = 16843955; // 0x10104b3
     field public static final int type = 16843169; // 0x10101a1
     field public static final int typeface = 16842902; // 0x1010096
     field public static final int uiOptions = 16843672; // 0x1010398
@@ -4490,6 +4490,7 @@
 
   public class KeyguardManager {
     method public deprecated void exitKeyguardSecurely(android.app.KeyguardManager.OnKeyguardExitResult);
+    method public android.content.Intent getConfirmDeviceCredentialIntent(java.lang.CharSequence, java.lang.CharSequence);
     method public boolean inKeyguardRestrictedInputMode();
     method public boolean isKeyguardLocked();
     method public boolean isKeyguardSecure();
@@ -7680,7 +7681,6 @@
     field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER";
     field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS";
     field public static final java.lang.String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
-    field public static final java.lang.String ACTION_CONFIRM_DEVICE_CREDENTIAL = "android.intent.action.CONFIRM_DEVICE_CREDENTIAL";
     field public static final java.lang.String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT";
     field public static final java.lang.String ACTION_CREATE_SHORTCUT = "android.intent.action.CREATE_SHORTCUT";
     field public static final java.lang.String ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED";
@@ -7824,7 +7824,6 @@
     field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
     field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
     field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
-    field public static final java.lang.String EXTRA_DETAILS = "android.intent.extra.DETAILS";
     field public static final java.lang.String EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE";
     field public static final int EXTRA_DOCK_STATE_CAR = 2; // 0x2
     field public static final int EXTRA_DOCK_STATE_DESK = 1; // 0x1
@@ -11655,7 +11654,7 @@
   public class AnimatedStateListDrawable extends android.graphics.drawable.StateListDrawable {
     ctor public AnimatedStateListDrawable();
     method public void addState(int[], android.graphics.drawable.Drawable, int);
-    method public void addTransition(int, int, android.graphics.drawable.Drawable, boolean);
+    method public void addTransition(int, int, T, boolean);
   }
 
   public class AnimatedVectorDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Animatable {
@@ -13586,7 +13585,7 @@
 
   public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
     ctor public InputMethodService();
-    method public boolean enableHardwareAcceleration();
+    method public deprecated boolean enableHardwareAcceleration();
     method public int getBackDisposition();
     method public int getCandidatesHiddenVisibility();
     method public android.view.inputmethod.InputBinding getCurrentInputBinding();
@@ -16717,6 +16716,7 @@
     method public void onPlaybackStateChanged(android.media.session.PlaybackState);
     method public void onQueueChanged(java.util.List<android.media.session.MediaSession.Track>);
     method public void onQueueTitleChanged(java.lang.CharSequence);
+    method public void onSessionDestroyed();
     method public void onSessionEvent(java.lang.String, android.os.Bundle);
     method public void onVolumeInfoChanged(android.media.session.MediaController.VolumeInfo);
   }
@@ -23026,9 +23026,9 @@
     method public boolean isUserRunning(android.os.UserHandle);
     method public boolean isUserRunningOrStopping(android.os.UserHandle);
     method public boolean setRestrictionsChallenge(java.lang.String);
-    method public void setUserRestriction(java.lang.String, boolean);
-    method public void setUserRestrictions(android.os.Bundle);
-    method public void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
+    method public deprecated void setUserRestriction(java.lang.String, boolean);
+    method public deprecated void setUserRestrictions(android.os.Bundle);
+    method public deprecated void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
     field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
     field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
     field public static final java.lang.String DISALLOW_APPS_CONTROL = "no_control_apps";
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index d9faf4c..b37f896 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -178,6 +178,13 @@
         public ControllerMonitor(ISessionController controller) {
             mController = controller;
         }
+
+        @Override
+        public void onSessionDestroyed() {
+            System.out.println("onSessionDestroyed. Enter q to quit.");
+
+        }
+
         @Override
         public void onEvent(String event, Bundle extras) {
             System.out.println("onSessionEvent event=" + event + ", extras=" + extras);
@@ -218,7 +225,7 @@
         void printUsageMessage() {
             try {
                 System.out.println("V2Monitoring session " + mController.getTag()
-                        + "...  available commands:");
+                        + "...  available commands: play, pause, next, previous");
             } catch (RemoteException e) {
                 System.out.println("Error trying to monitor session!");
             }
@@ -250,6 +257,14 @@
                         addNewline = false;
                     } else if ("q".equals(line) || "quit".equals(line)) {
                         break;
+                    } else if ("play".equals(line)) {
+                        mController.play();
+                    } else if ("pause".equals(line)) {
+                        mController.pause();
+                    } else if ("next".equals(line)) {
+                        mController.next();
+                    } else if ("previous".equals(line)) {
+                        mController.previous();
                     } else {
                         System.out.println("Invalid command: " + line);
                     }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 9bbd544..21c8b75 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6119,6 +6119,7 @@
      *
      * @hide
      */
+    @SystemApi
     public interface TranslucentConversionListener {
         /**
          * Callback made following {@link Activity#convertToTranslucent} once all visible Activities
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1b82d8e..d9ea671 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1807,9 +1807,13 @@
         }
     }
 
-    public void installSystemApplicationInfo(ApplicationInfo info) {
+    public void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
         synchronized (this) {
-            getSystemContext().installSystemApplicationInfo(info);
+            getSystemContext().installSystemApplicationInfo(info, classLoader);
+
+            // The code package for "android" in the system server needs
+            // to be the system context's package.
+            mPackages.put("android", new WeakReference<LoadedApk>(getSystemContext().mPackageInfo));
 
             // give ourselves a default profiler
             mProfiler = new Profiler();
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index f7c0d23..7d2f677 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -239,7 +239,7 @@
         return mWindow;
     }
 
-    protected ViewGroup getDecor() {
+    public ViewGroup getDecor() {
         return (mWindow == null) ? null : (ViewGroup) mWindow.getDecorView();
     }
 
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 4f3b02c..613e248 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -17,9 +17,11 @@
 
 import android.os.Bundle;
 import android.os.ResultReceiver;
+import android.transition.Transition;
 import android.util.ArrayMap;
 import android.util.SparseArray;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.Window;
 
 import java.lang.ref.WeakReference;
@@ -245,13 +247,23 @@
         } else {
             if (!mHasExited) {
                 mHasExited = true;
+                Transition enterViewsTransition = null;
+                ViewGroup decor = null;
                 if (mEnterTransitionCoordinator != null) {
+                    enterViewsTransition = mEnterTransitionCoordinator.getEnterViewsTransition();
+                    decor = mEnterTransitionCoordinator.getDecor();
                     mEnterTransitionCoordinator.cancelEnter();
                     mEnterTransitionCoordinator = null;
+                    if (enterViewsTransition != null && decor != null) {
+                        enterViewsTransition.pause(decor);
+                    }
                 }
 
                 ExitTransitionCoordinator exitCoordinator =
                         new ExitTransitionCoordinator(activity, mEnteringNames, null, null, true);
+                if (enterViewsTransition != null && decor != null) {
+                    enterViewsTransition.resume(decor);
+                }
                 exitCoordinator.startExit(activity.mResultCode, activity.mResultData);
             }
             return true;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4cf8cb4..da343ac 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2301,8 +2301,8 @@
         }
     }
 
-    void installSystemApplicationInfo(ApplicationInfo info) {
-        mPackageInfo.installSystemApplicationInfo(info);
+    void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
+        mPackageInfo.installSystemApplicationInfo(info, classLoader);
     }
 
     final void scheduleFinalCleanup(String who, String what) {
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 54c5bc4..75ecbd9 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -59,6 +59,7 @@
     private boolean mIsViewsTransitionComplete;
     private boolean mIsSharedElementTransitionComplete;
     private ArrayList<Matrix> mSharedElementParentMatrices;
+    private Transition mEnterViewsTransition;
 
     public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
             ArrayList<String> sharedElementNames, boolean isReturning) {
@@ -104,6 +105,10 @@
         triggerViewsReady(mapNamedElements(accepted, localNames));
     }
 
+    public Transition getEnterViewsTransition() {
+        return mEnterViewsTransition;
+    }
+
     @Override
     protected void viewsReady(ArrayMap<String, View> sharedElements) {
         super.viewsReady(sharedElements);
@@ -399,10 +404,17 @@
                 viewTransitionComplete();
             } else {
                 viewsTransition.forceVisibility(View.INVISIBLE, true);
-                setTransitionAlpha(mTransitioningViews, 1);
                 viewsTransition.addListener(new ContinueTransitionListener() {
                     @Override
+                    public void onTransitionStart(Transition transition) {
+                        mEnterViewsTransition = transition;
+                        setTransitionAlpha(mTransitioningViews, 1);
+                        super.onTransitionStart(transition);
+                    }
+
+                    @Override
                     public void onTransitionEnd(Transition transition) {
+                        mEnterViewsTransition = null;
                         transition.removeListener(this);
                         viewTransitionComplete();
                         super.onTransitionEnd(transition);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 4b65934..e8f6818 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -66,7 +66,7 @@
     boolean setZenModeConfig(in ZenModeConfig config);
     oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions);
     oneway void requestZenModeConditions(in IConditionListener callback, int relevance);
-    oneway void setZenModeCondition(in Uri conditionId);
+    oneway void setZenModeCondition(in Condition condition);
     oneway void setAutomaticZenModeConditions(in Uri[] conditionIds);
     Condition[] getAutomaticZenModeConditions();
 }
\ No newline at end of file
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index db91742a..50e3a10 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.content.Intent;
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.IBinder;
@@ -24,7 +25,7 @@
 import android.view.WindowManagerGlobal;
 
 /**
- * Class that can be used to lock and unlock the keyboard. Get an instance of this 
+ * Class that can be used to lock and unlock the keyboard. Get an instance of this
  * class by calling {@link android.content.Context#getSystemService(java.lang.String)}
  * with argument {@link android.content.Context#KEYGUARD_SERVICE}. The
  * actual class to control the keyboard locking is
@@ -34,6 +35,45 @@
     private IWindowManager mWM;
 
     /**
+     * Intent used to prompt user for device credentials.
+     * @hide
+     */
+    public static final String ACTION_CONFIRM_DEVICE_CREDENTIAL =
+            "android.app.action.CONFIRM_DEVICE_CREDENTIAL";
+
+    /**
+     * A CharSequence dialog title to show to the user when used with a
+     * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
+     * @hide
+     */
+    public static final String EXTRA_TITLE = "android.app.extra.TITLE";
+
+    /**
+     * A CharSequence description to show to the user when used with
+     * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
+     * @hide
+     */
+    public static final String EXTRA_DESCRIPTION = "android.app.extra.DESCRIPTION";
+
+    /**
+     * Get an intent to prompt the user to confirm credentials (pin, pattern or password)
+     * for the current user of the device. The caller is expected to launch this activity using
+     * {@link android.app.Activity#startActivityForResult(Intent, int)} and check for
+     * {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge.
+     *
+     * @return the intent for launching the activity or null if no password is required.
+     **/
+    public Intent getConfirmDeviceCredentialIntent(CharSequence title, CharSequence description) {
+        if (!isKeyguardSecure()) return null;
+        Intent intent = new Intent(ACTION_CONFIRM_DEVICE_CREDENTIAL);
+        intent.putExtra(EXTRA_TITLE, title);
+        intent.putExtra(EXTRA_DESCRIPTION, description);
+        // For security reasons, only allow this to come from system settings.
+        intent.setPackage("com.android.settings");
+        return intent;
+    }
+
+    /**
      * @deprecated Use {@link android.view.WindowManager.LayoutParams#FLAG_DISMISS_KEYGUARD}
      * and/or {@link android.view.WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED}
      * instead; this allows you to seamlessly hide the keyguard as your application
@@ -58,7 +98,7 @@
          *
          * A good place to call this is from {@link android.app.Activity#onResume()}
          *
-         * Note: This call has no effect while any {@link android.app.admin.DevicePolicyManager} 
+         * Note: This call has no effect while any {@link android.app.admin.DevicePolicyManager}
          * is enabled that requires a password.
          *
          * <p>This method requires the caller to hold the permission
@@ -121,7 +161,7 @@
      * permissions be requested.
      *
      * Enables you to lock or unlock the keyboard. Get an instance of this class by
-     * calling {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}. 
+     * calling {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
      * This class is wrapped by {@link android.app.KeyguardManager KeyguardManager}.
      * @param tag A tag that informally identifies who you are (for debugging who
      *   is disabling he keyguard).
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 24c2835..fcfc1c49 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -199,9 +199,10 @@
     /**
      * Sets application info about the system package.
      */
-    void installSystemApplicationInfo(ApplicationInfo info) {
+    void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
         assert info.packageName.equals("android");
         mApplicationInfo = info;
+        mClassLoader = classLoader;
     }
 
     public String getPackageName() {
@@ -262,10 +263,6 @@
                 if (!Objects.equals(mPackageName, ActivityThread.currentPackageName())) {
                     final String isa = VMRuntime.getRuntime().vmInstructionSet();
                     try {
-                        // TODO: We can probably do away with the isa argument since
-                        // the AM and PM have enough information to figure this out
-                        // themselves. If we do need it, we should match it against the
-                        // list of devices ISAs before sending it down to installd.
                         ActivityThread.getPackageManager().performDexOptIfNeeded(mPackageName, isa);
                     } catch (RemoteException re) {
                         // Ignored.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index abe4f0a..61e105b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1419,21 +1419,6 @@
     public static final String ACTION_UNINSTALL_PACKAGE = "android.intent.action.UNINSTALL_PACKAGE";
 
     /**
-     * Activity Action: Prompt the user to confirm credentials (pin, pattern or password)
-     * for the current user of the device. Launch this activity using
-     * {@link android.app.Activity#startActivityForResult(Intent, int)} and check if the
-     * result is {@link android.app.Activity#RESULT_OK} for a successful response to the
-     * challenge.<p/>
-     * This intent is handled by the system at a high priority and applications cannot intercept
-     * it.<p/>
-     * You can use {@link android.app.KeyguardManager#isKeyguardSecure()} to determine if the user will be
-     * prompted.
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_CONFIRM_DEVICE_CREDENTIAL = "android.intent.action.CONFIRM_DEVICE_CREDENTIAL";
-
-
-    /**
      * Specify whether the package should be uninstalled for all users.
      * @hide because these should not be part of normal application flow.
      */
@@ -3192,17 +3177,11 @@
 
     /**
      * A CharSequence dialog title to provide to the user when used with a
-     * {@link #ACTION_CHOOSER} or {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
+     * {@link #ACTION_CHOOSER}.
      */
     public static final String EXTRA_TITLE = "android.intent.extra.TITLE";
 
     /**
-     * A CharSequence description to provide to the user when used with
-     * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
-     */
-    public static final String EXTRA_DETAILS = "android.intent.extra.DETAILS";
-
-    /**
      * A Parcelable[] of {@link Intent} or
      * {@link android.content.pm.LabeledIntent} objects as set with
      * {@link #putExtra(String, Parcelable[])} of additional activities to place
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index af574db..4e7da48 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -70,7 +70,7 @@
 
     /**
      * Additional flag for {@link #protectionLevel}, corresponding
-     * to the <code>development</code> value of
+     * to the <code>appop</code> value of
      * {@link android.R.attr#protectionLevel}.
      */
     public static final int PROTECTION_FLAG_APPOP = 0x40;
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 8423d09..ba811b7 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -640,10 +640,13 @@
      * You can call this to try to enable hardware accelerated drawing for
      * your IME. This must be set before {@link #onCreate}, so you
      * will typically call it in your constructor.  It is not always possible
-     * to use hardware acclerated drawing in an IME (for example on low-end
+     * to use hardware accelerated drawing in an IME (for example on low-end
      * devices that do not have the resources to support this), so the call
      * returns true if it succeeds otherwise false if you will need to draw
      * in software.  You must be able to handle either case.
+     *
+     * @deprecated Starting in API 21, hardware acceleration is always enabled
+     *             on capable devices.
      */
     public boolean enableHardwareAcceleration() {
         if (mWindow != null) {
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 47b74ab..6160bc2 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.net.ProxyInfo;
 import android.os.Parcelable;
 import android.os.Parcel;
@@ -124,7 +125,7 @@
      *
      * @return The interface name set for this link or {@code null}.
      */
-    public String getInterfaceName() {
+    public @Nullable String getInterfaceName() {
         return mIfaceName;
     }
 
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index ee4d45e..5815fa6 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -59,5 +59,5 @@
 
     void registerLockscreenDispatch(INfcLockscreenDispatch lockscreenDispatch, in int[] techList);
     void addNfcUnlockHandler(INfcUnlockHandler unlockHandler, in int[] techList);
-    void removeNfcUnlockHandler(IBinder b);
+    void removeNfcUnlockHandler(INfcUnlockHandler unlockHandler);
 }
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index dde2cf1..6bd5a32 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -311,7 +311,7 @@
 
     final NfcActivityManager mNfcActivityManager;
     final Context mContext;
-    final HashMap<NfcUnlockHandler, IBinder> mNfcUnlockHandlers;
+    final HashMap<NfcUnlockHandler, INfcUnlockHandler> mNfcUnlockHandlers;
     final Object mLock;
 
     /**
@@ -542,7 +542,7 @@
     NfcAdapter(Context context) {
         mContext = context;
         mNfcActivityManager = new NfcActivityManager(this);
-        mNfcUnlockHandlers = new HashMap<NfcUnlockHandler, IBinder>();
+        mNfcUnlockHandlers = new HashMap<NfcUnlockHandler, INfcUnlockHandler>();
         mLock = new Object();
     }
 
@@ -1498,27 +1498,37 @@
      * <p /> The parameter {@code tagTechnologies} determines which Tag technologies will be polled for
      * at the lockscreen. Polling for less tag technologies reduces latency, and so it is
      * strongly recommended to only provide the Tag technologies that the handler is expected to
-     * receive.
+     * receive. There must be at least one tag technology provided, otherwise the unlock handler
+     * is ignored.
      *
      * @hide
      */
     @SystemApi
     public boolean addNfcUnlockHandler(final NfcUnlockHandler unlockHandler,
                                        String[] tagTechnologies) {
-        try {
-            INfcUnlockHandler.Stub iHandler = new INfcUnlockHandler.Stub() {
-                @Override
-                public boolean onUnlockAttempted(Tag tag) throws RemoteException {
-                    return unlockHandler.onUnlockAttempted(tag);
-                }
-            };
+        // If there are no tag technologies, don't bother adding unlock handler
+        if (tagTechnologies.length == 0) {
+            return false;
+        }
 
+        try {
             synchronized (mLock) {
                 if (mNfcUnlockHandlers.containsKey(unlockHandler)) {
-                    return true;
+                    // update the tag technologies
+                    sService.removeNfcUnlockHandler(mNfcUnlockHandlers.get(unlockHandler));
+                    mNfcUnlockHandlers.remove(unlockHandler);
                 }
-                sService.addNfcUnlockHandler(iHandler, Tag.getTechCodesFromStrings(tagTechnologies));
-                mNfcUnlockHandlers.put(unlockHandler, iHandler.asBinder());
+
+                INfcUnlockHandler.Stub iHandler = new INfcUnlockHandler.Stub() {
+                    @Override
+                    public boolean onUnlockAttempted(Tag tag) throws RemoteException {
+                        return unlockHandler.onUnlockAttempted(tag);
+                    }
+                };
+
+                sService.addNfcUnlockHandler(iHandler,
+                        Tag.getTechCodesFromStrings(tagTechnologies));
+                mNfcUnlockHandlers.put(unlockHandler, iHandler);
             }
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
@@ -1542,8 +1552,7 @@
         try {
             synchronized (mLock) {
                 if (mNfcUnlockHandlers.containsKey(unlockHandler)) {
-                    sService.removeNfcUnlockHandler(mNfcUnlockHandlers.get(unlockHandler));
-                    mNfcUnlockHandlers.remove(unlockHandler);
+                    sService.removeNfcUnlockHandler(mNfcUnlockHandlers.remove(unlockHandler));
                 }
 
                 return true;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 10c955e..9d1a7bc 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -499,7 +499,12 @@
      * Sets all the user-wide restrictions for this user.
      * Requires the MANAGE_USERS permission.
      * @param restrictions the Bundle containing all the restrictions.
+     * @deprecated use {@link android.app.admin.DevicePolicyManager#addUserRestriction(
+     * android.content.ComponentName, String)} or
+     * {@link android.app.admin.DevicePolicyManager#clearUserRestriction(
+     * android.content.ComponentName, String)} instead.
      */
+    @Deprecated
     public void setUserRestrictions(Bundle restrictions) {
         setUserRestrictions(restrictions, Process.myUserHandle());
     }
@@ -509,7 +514,12 @@
      * Requires the MANAGE_USERS permission.
      * @param restrictions the Bundle containing all the restrictions.
      * @param userHandle the UserHandle of the user for whom to set the restrictions.
+     * @deprecated use {@link android.app.admin.DevicePolicyManager#addUserRestriction(
+     * android.content.ComponentName, String)} or
+     * {@link android.app.admin.DevicePolicyManager#clearUserRestriction(
+     * android.content.ComponentName, String)} instead.
      */
+    @Deprecated
     public void setUserRestrictions(Bundle restrictions, UserHandle userHandle) {
         try {
             mService.setUserRestrictions(restrictions, userHandle.getIdentifier());
@@ -523,7 +533,12 @@
      * Requires the MANAGE_USERS permission.
      * @param key the key of the restriction
      * @param value the value for the restriction
+     * @deprecated use {@link android.app.admin.DevicePolicyManager#addUserRestriction(
+     * android.content.ComponentName, String)} or
+     * {@link android.app.admin.DevicePolicyManager#clearUserRestriction(
+     * android.content.ComponentName, String)} instead.
      */
+    @Deprecated
     public void setUserRestriction(String key, boolean value) {
         Bundle bundle = getUserRestrictions();
         bundle.putBoolean(key, value);
@@ -537,7 +552,12 @@
      * @param key the key of the restriction
      * @param value the value for the restriction
      * @param userHandle the user whose restriction is to be changed.
+     * @deprecated use {@link android.app.admin.DevicePolicyManager#addUserRestriction(
+     * android.content.ComponentName, String)} or
+     * {@link android.app.admin.DevicePolicyManager#clearUserRestriction(
+     * android.content.ComponentName, String)} instead.
      */
+    @Deprecated
     public void setUserRestriction(String key, boolean value, UserHandle userHandle) {
         Bundle bundle = getUserRestrictions(userHandle);
         bundle.putBoolean(key, value);
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index acd7942..e95e6e2 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -258,6 +258,7 @@
      */
     public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
         if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) {
+            onUnbindPreferences();
             mHavePrefs = true;
             if (mInitDone) {
                 postBindPreferences();
@@ -350,6 +351,10 @@
     }
 
     /** @hide */
+    protected void onUnbindPreferences() {
+    }
+
+    /** @hide */
     public ListView getListView() {
         ensureList();
         return mList;
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index cc09653..872f911 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -73,9 +73,14 @@
     private static final String CONDITION_TAG = "condition";
     private static final String CONDITION_ATT_COMPONENT = "component";
     private static final String CONDITION_ATT_ID = "id";
+    private static final String CONDITION_ATT_SUMMARY = "summary";
+    private static final String CONDITION_ATT_LINE1 = "line1";
+    private static final String CONDITION_ATT_LINE2 = "line2";
+    private static final String CONDITION_ATT_ICON = "icon";
+    private static final String CONDITION_ATT_STATE = "state";
+    private static final String CONDITION_ATT_FLAGS = "flags";
 
     private static final String EXIT_CONDITION_TAG = "exitCondition";
-    private static final String EXIT_CONDITION_ATT_ID = "id";
     private static final String EXIT_CONDITION_ATT_COMPONENT = "component";
 
     public boolean allowCalls;
@@ -83,13 +88,13 @@
     public int allowFrom = SOURCE_ANYONE;
 
     public String sleepMode;
-    public int sleepStartHour;
-    public int sleepStartMinute;
+    public int sleepStartHour;   // 0-23
+    public int sleepStartMinute; // 0-59
     public int sleepEndHour;
     public int sleepEndMinute;
     public ComponentName[] conditionComponents;
     public Uri[] conditionIds;
-    public Uri exitConditionId;
+    public Condition exitCondition;
     public ComponentName exitConditionComponent;
 
     public ZenModeConfig() { }
@@ -115,7 +120,7 @@
             source.readTypedArray(conditionIds, Uri.CREATOR);
         }
         allowFrom = source.readInt();
-        exitConditionId = source.readParcelable(null);
+        exitCondition = source.readParcelable(null);
         exitConditionComponent = source.readParcelable(null);
     }
 
@@ -146,7 +151,7 @@
             dest.writeInt(0);
         }
         dest.writeInt(allowFrom);
-        dest.writeParcelable(exitConditionId, 0);
+        dest.writeParcelable(exitCondition, 0);
         dest.writeParcelable(exitConditionComponent, 0);
     }
 
@@ -163,7 +168,7 @@
             .append(conditionComponents == null ? null : TextUtils.join(",", conditionComponents))
             .append(",conditionIds=")
             .append(conditionIds == null ? null : TextUtils.join(",", conditionIds))
-            .append(",exitConditionId=").append(exitConditionId)
+            .append(",exitCondition=").append(exitCondition)
             .append(",exitConditionComponent=").append(exitConditionComponent)
             .append(']').toString();
     }
@@ -196,7 +201,7 @@
                 && other.sleepEndMinute == sleepEndMinute
                 && Objects.deepEquals(other.conditionComponents, conditionComponents)
                 && Objects.deepEquals(other.conditionIds, conditionIds)
-                && Objects.equals(other.exitConditionId, exitConditionId)
+                && Objects.equals(other.exitCondition, exitCondition)
                 && Objects.equals(other.exitConditionComponent, exitConditionComponent);
     }
 
@@ -205,7 +210,7 @@
         return Objects.hash(allowCalls, allowMessages, allowFrom, sleepMode,
                 sleepStartHour, sleepStartMinute, sleepEndHour, sleepEndMinute,
                 Arrays.hashCode(conditionComponents), Arrays.hashCode(conditionIds),
-                exitConditionId, exitConditionComponent);
+                exitCondition, exitConditionComponent);
     }
 
     public boolean isValid() {
@@ -294,9 +299,11 @@
                         conditionIds.add(conditionId);
                     }
                 } else if (EXIT_CONDITION_TAG.equals(tag)) {
-                    rt.exitConditionId = safeUri(parser, EXIT_CONDITION_ATT_ID);
-                    rt.exitConditionComponent =
-                            safeComponentName(parser, EXIT_CONDITION_ATT_COMPONENT);
+                    rt.exitCondition = readConditionXml(parser);
+                    if (rt.exitCondition != null) {
+                        rt.exitConditionComponent =
+                                safeComponentName(parser, EXIT_CONDITION_ATT_COMPONENT);
+                    }
                 }
             }
         }
@@ -333,16 +340,42 @@
                 out.endTag(null, CONDITION_TAG);
             }
         }
-        if (exitConditionId != null && exitConditionComponent != null) {
+        if (exitCondition != null && exitConditionComponent != null) {
             out.startTag(null, EXIT_CONDITION_TAG);
-            out.attribute(null, EXIT_CONDITION_ATT_ID, exitConditionId.toString());
             out.attribute(null, EXIT_CONDITION_ATT_COMPONENT,
                     exitConditionComponent.flattenToString());
+            writeConditionXml(exitCondition, out);
             out.endTag(null, EXIT_CONDITION_TAG);
         }
         out.endTag(null, ZEN_TAG);
     }
 
+    public static Condition readConditionXml(XmlPullParser parser) {
+        final Uri id = safeUri(parser, CONDITION_ATT_ID);
+        final String summary = parser.getAttributeValue(null, CONDITION_ATT_SUMMARY);
+        final String line1 = parser.getAttributeValue(null, CONDITION_ATT_LINE1);
+        final String line2 = parser.getAttributeValue(null, CONDITION_ATT_LINE2);
+        final int icon = safeInt(parser, CONDITION_ATT_ICON, -1);
+        final int state = safeInt(parser, CONDITION_ATT_STATE, -1);
+        final int flags = safeInt(parser, CONDITION_ATT_FLAGS, -1);
+        try {
+            return new Condition(id, summary, line1, line2, icon, state, flags);
+        } catch (IllegalArgumentException e) {
+            Slog.w(TAG, "Unable to read condition xml", e);
+            return null;
+        }
+    }
+
+    public static void writeConditionXml(Condition c, XmlSerializer out) throws IOException {
+        out.attribute(null, CONDITION_ATT_ID, c.id.toString());
+        out.attribute(null, CONDITION_ATT_SUMMARY, c.summary);
+        out.attribute(null, CONDITION_ATT_LINE1, c.line1);
+        out.attribute(null, CONDITION_ATT_LINE2, c.line2);
+        out.attribute(null, CONDITION_ATT_ICON, Integer.toString(c.icon));
+        out.attribute(null, CONDITION_ATT_STATE, Integer.toString(c.state));
+        out.attribute(null, CONDITION_ATT_FLAGS, Integer.toString(c.flags));
+    }
+
     public static boolean isValidHour(int val) {
         return val >= 0 && val < 24;
     }
@@ -403,21 +436,31 @@
         }
     };
 
-    // Built-in countdown conditions, e.g. condition://android/countdown/1399917958951
+    public DowntimeInfo toDowntimeInfo() {
+        final DowntimeInfo downtime = new DowntimeInfo();
+        downtime.startHour = sleepStartHour;
+        downtime.startMinute = sleepStartMinute;
+        downtime.endHour = sleepEndHour;
+        downtime.endMinute = sleepEndMinute;
+        return downtime;
+    }
 
-    private static final String COUNTDOWN_AUTHORITY = "android";
+    // For built-in conditions
+    private static final String SYSTEM_AUTHORITY = "android";
+
+    // Built-in countdown conditions, e.g. condition://android/countdown/1399917958951
     private static final String COUNTDOWN_PATH = "countdown";
 
     public static Uri toCountdownConditionId(long time) {
         return new Uri.Builder().scheme(Condition.SCHEME)
-                .authority(COUNTDOWN_AUTHORITY)
+                .authority(SYSTEM_AUTHORITY)
                 .appendPath(COUNTDOWN_PATH)
                 .appendPath(Long.toString(time))
                 .build();
     }
 
     public static long tryParseCountdownConditionId(Uri conditionId) {
-        if (!Condition.isValidId(conditionId, COUNTDOWN_AUTHORITY)) return 0;
+        if (!Condition.isValidId(conditionId, SYSTEM_AUTHORITY)) return 0;
         if (conditionId.getPathSegments().size() != 2
                 || !COUNTDOWN_PATH.equals(conditionId.getPathSegments().get(0))) return 0;
         try {
@@ -431,4 +474,68 @@
     public static boolean isValidCountdownConditionId(Uri conditionId) {
         return tryParseCountdownConditionId(conditionId) != 0;
     }
+
+    // Built-in downtime conditions, e.g. condition://android/downtime?start=10.00&end=7.00
+    private static final String DOWNTIME_PATH = "downtime";
+
+    public static Uri toDowntimeConditionId(DowntimeInfo downtime) {
+        return new Uri.Builder().scheme(Condition.SCHEME)
+                .authority(SYSTEM_AUTHORITY)
+                .appendPath(DOWNTIME_PATH)
+                .appendQueryParameter("start", downtime.startHour + "." + downtime.startMinute)
+                .appendQueryParameter("end", downtime.endHour + "." + downtime.endMinute)
+                .build();
+    }
+
+    public static DowntimeInfo tryParseDowntimeConditionId(Uri conditionId) {
+        if (!Condition.isValidId(conditionId, SYSTEM_AUTHORITY)
+                || conditionId.getPathSegments().size() != 1
+                || !DOWNTIME_PATH.equals(conditionId.getPathSegments().get(0))) {
+            return null;
+        }
+        final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start"));
+        final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end"));
+        if (start == null || end == null) return null;
+        final DowntimeInfo downtime = new DowntimeInfo();
+        downtime.startHour = start[0];
+        downtime.startMinute = start[1];
+        downtime.endHour = end[0];
+        downtime.endMinute = end[1];
+        return downtime;
+    }
+
+    private static int[] tryParseHourAndMinute(String value) {
+        if (TextUtils.isEmpty(value)) return null;
+        final int i = value.indexOf('.');
+        if (i < 1 || i >= value.length() - 1) return null;
+        final int hour = tryParseInt(value.substring(0, i), -1);
+        final int minute = tryParseInt(value.substring(i + 1), -1);
+        return isValidHour(hour) && isValidMinute(minute) ? new int[] { hour, minute } : null;
+    }
+
+    public static boolean isValidDowntimeConditionId(Uri conditionId) {
+        return tryParseDowntimeConditionId(conditionId) != null;
+    }
+
+    public static class DowntimeInfo {
+        public int startHour;   // 0-23
+        public int startMinute; // 0-59
+        public int endHour;
+        public int endMinute;
+
+        @Override
+        public int hashCode() {
+            return 0;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof DowntimeInfo)) return false;
+            final DowntimeInfo other = (DowntimeInfo) o;
+            return startHour == other.startHour
+                    && startMinute == other.startMinute
+                    && endHour == other.endHour
+                    && endMinute == other.endMinute;
+        }
+    }
 }
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 51f07ec..5fe9194 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -69,12 +69,6 @@
             "[" + getClass().getSimpleName() + "]";
     private static final boolean DEBUG = false;
 
-    // Temporary workaround to allow current trust agent implementations to continue working.
-    // This and the code guarded by this should be removed before shipping.
-    // If true, calls setManagingTrust(true) after onCreate, if it wasn't already set.
-    // TODO: Remove this once all agents are updated.
-    private static final boolean SET_MANAGED_FOR_LEGACY_AGENTS = true;
-
     /**
      * The {@link Intent} that must be declared as handled by the service.
      */
@@ -130,11 +124,6 @@
 
     @Override
     public void onCreate() {
-        // TODO: Remove this once all agents are updated.
-        if (SET_MANAGED_FOR_LEGACY_AGENTS) {
-            setManagingTrust(true);
-        }
-
         super.onCreate();
         ComponentName component = new ComponentName(this, getClass());
         try {
@@ -175,7 +164,7 @@
      * set.
      *
      * @param options Option feature bundle.
-     * @return true if the {@link #TrustAgentService()} supports this feature.
+     * @return true if the {@link TrustAgentService} supports this feature.
      */
     public boolean onSetTrustAgentFeaturesEnabled(Bundle options) {
         return false;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index a36f06c7..7dce348 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -273,16 +273,22 @@
                 // Draw all leading margin spans.  Adjust left or right according
                 // to the paragraph direction of the line.
                 final int length = spans.length;
+                boolean useFirstLineMargin = isFirstParaLine;
+                for (int n = 0; n < length; n++) {
+                    if (spans[n] instanceof LeadingMarginSpan2) {
+                        int count = ((LeadingMarginSpan2) spans[n]).getLeadingMarginLineCount();
+                        int startLine = getLineForOffset(sp.getSpanStart(spans[n]));
+                        // if there is more than one LeadingMarginSpan2, use
+                        // the count that is greatest
+                        if (i < startLine + count) {
+                            useFirstLineMargin = true;
+                            break;
+                        }
+                    }
+                }
                 for (int n = 0; n < length; n++) {
                     if (spans[n] instanceof LeadingMarginSpan) {
                         LeadingMarginSpan margin = (LeadingMarginSpan) spans[n];
-                        boolean useFirstLineMargin = isFirstParaLine;
-                        if (margin instanceof LeadingMarginSpan2) {
-                            int count = ((LeadingMarginSpan2) margin).getLeadingMarginLineCount();
-                            int startLine = getLineForOffset(sp.getSpanStart(margin));
-                            useFirstLineMargin = i < startLine + count;
-                        }
-
                         if (dir == DIR_RIGHT_TO_LEFT) {
                             margin.drawLeadingMargin(canvas, paint, right, dir, ltop,
                                                      lbaseline, lbottom, buf,
@@ -1535,15 +1541,18 @@
         boolean isFirstParaLine = lineStart == 0 ||
             spanned.charAt(lineStart - 1) == '\n';
 
+        boolean useFirstLineMargin = isFirstParaLine;
+        for (int i = 0; i < spans.length; i++) {
+            if (spans[i] instanceof LeadingMarginSpan2) {
+                int spStart = spanned.getSpanStart(spans[i]);
+                int spanLine = getLineForOffset(spStart);
+                int count = ((LeadingMarginSpan2) spans[i]).getLeadingMarginLineCount();
+                // if there is more than one LeadingMarginSpan2, use the count that is greatest
+                useFirstLineMargin |= line < spanLine + count;
+            }
+        }
         for (int i = 0; i < spans.length; i++) {
             LeadingMarginSpan span = spans[i];
-            boolean useFirstLineMargin = isFirstParaLine;
-            if (span instanceof LeadingMarginSpan2) {
-                int spStart = spanned.getSpanStart(span);
-                int spanLine = getLineForOffset(spStart);
-                int count = ((LeadingMarginSpan2)span).getLeadingMarginLineCount();
-                useFirstLineMargin = line < spanLine + count;
-            }
             margin += span.getLeadingMargin(useFirstLineMargin);
         }
 
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 4144a75..aecf488 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -201,13 +201,12 @@
                     restWidth -= sp[i].getLeadingMargin(false);
 
                     // LeadingMarginSpan2 is odd.  The count affects all
-                    // leading margin spans, not just this particular one,
-                    // and start from the top of the span, not the top of the
-                    // paragraph.
+                    // leading margin spans, not just this particular one
                     if (lms instanceof LeadingMarginSpan2) {
                         LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
                         int lmsFirstLine = getLineForOffset(spanned.getSpanStart(lms2));
-                        firstWidthLineLimit = lmsFirstLine + lms2.getLeadingMarginLineCount();
+                        firstWidthLineLimit = Math.max(firstWidthLineLimit,
+                                lmsFirstLine + lms2.getLeadingMarginLineCount());
                     }
                 }
 
diff --git a/core/java/android/text/style/LeadingMarginSpan.java b/core/java/android/text/style/LeadingMarginSpan.java
index 2f429ff..96a7cd9 100644
--- a/core/java/android/text/style/LeadingMarginSpan.java
+++ b/core/java/android/text/style/LeadingMarginSpan.java
@@ -28,6 +28,9 @@
  * margin spans on a single paragraph; they will be rendered in order, each
  * adding its margin to the ones before it. The leading margin is on the right
  * for lines in a right-to-left paragraph.
+ * <p>
+ * LeadingMarginSpans should be attached from the first character to the last
+ * character of a single paragraph.
  */
 public interface LeadingMarginSpan
 extends ParagraphStyle
@@ -69,18 +72,22 @@
 
 
     /**
-     * An extended version of {@link LeadingMarginSpan}, which allows
-     * the implementor to specify the number of lines of text to which
-     * this object is attached that the "first line of paragraph" margin
-     * width will be applied to.
+     * An extended version of {@link LeadingMarginSpan}, which allows the
+     * implementor to specify the number of lines of the paragraph to which
+     * this object is attached that the "first line of paragraph" margin width
+     * will be applied to.
+     * <p>
+     * There should only be one LeadingMarginSpan2 per paragraph. The leading
+     * margin line count affects all LeadingMarginSpans in the paragraph,
+     * adjusting the number of lines to which the first line margin is applied.
+     * <p>
+     * As with LeadingMarginSpans, LeadingMarginSpan2s should be attached from
+     * the beginning to the end of a paragraph.
      */
     public interface LeadingMarginSpan2 extends LeadingMarginSpan, WrapTogetherSpan {
         /**
-         * Returns the number of lines of text to which this object is
+         * Returns the number of lines of the paragraph to which this object is
          * attached that the "first line" margin will apply to.
-         * Note that if this returns N, the first N lines of the region,
-         * not the first N lines of each paragraph, will be given the
-         * special margin width.
          */
         public int getLeadingMarginLineCount();
     };
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index ec589a5..c7ffedc 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -72,12 +72,17 @@
     private static WebViewFactoryProvider sProviderInstance;
     private static final Object sProviderLock = new Object();
     private static boolean sAddressSpaceReserved = false;
+    private static PackageInfo sPackageInfo;
 
     public static String getWebViewPackageName() {
         return AppGlobals.getInitialApplication().getString(
                 com.android.internal.R.string.config_webViewPackageName);
     }
 
+    public static PackageInfo getLoadedPackageInfo() {
+        return sPackageInfo;
+    }
+
     static WebViewFactoryProvider getProvider() {
         synchronized (sProviderLock) {
             // For now the main purpose of this function (and the factory abstraction) is to keep
@@ -125,9 +130,9 @@
         try {
             // First fetch the package info so we can log the webview package version.
             String packageName = getWebViewPackageName();
-            PackageInfo pi = initialApplication.getPackageManager().getPackageInfo(packageName, 0);
-            Log.i(LOGTAG, "Loading " + packageName + " version " + pi.versionName +
-                          " (code " + pi.versionCode + ")");
+            sPackageInfo = initialApplication.getPackageManager().getPackageInfo(packageName, 0);
+            Log.i(LOGTAG, "Loading " + packageName + " version " + sPackageInfo.versionName +
+                          " (code " + sPackageInfo.versionCode + ")");
 
             // Construct a package context to load the Java code into the current app.
             Context webViewContext = initialApplication.createPackageContext(packageName,
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index ccd0480..29a6a7d 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -53,6 +53,8 @@
 
     private int mBasePadding;
     private int mCheckMarkWidth;
+    private int mCheckMarkGravity = Gravity.END;
+
     private boolean mNeedRequestlayout;
 
     private static final int[] CHECKED_STATE_SET = {
@@ -83,15 +85,17 @@
         }
 
         mCheckMarkTintMode = Drawable.parseTintMode(a.getInt(
-                R.styleable.CompoundButton_buttonTintMode, -1), mCheckMarkTintMode);
+                R.styleable.CheckedTextView_checkMarkTintMode, -1), mCheckMarkTintMode);
 
-        if (a.hasValue(R.styleable.CompoundButton_buttonTint)) {
-            mCheckMarkTintList = a.getColorStateList(R.styleable.CompoundButton_buttonTint);
+        if (a.hasValue(R.styleable.CheckedTextView_checkMarkTint)) {
+            mCheckMarkTintList = a.getColorStateList(R.styleable.CheckedTextView_checkMarkTint);
             mHasCheckMarkTint = true;
 
             applyCheckMarkTint();
         }
 
+        mCheckMarkGravity = a.getInt(R.styleable.CheckedTextView_checkMarkGravity, Gravity.END);
+
         boolean checked = a.getBoolean(R.styleable.CheckedTextView_checked, false);
         setChecked(checked);
 
@@ -293,7 +297,7 @@
     @Override
     protected void internalSetPadding(int left, int top, int right, int bottom) {
         super.internalSetPadding(left, top, right, bottom);
-        setBasePadding(isLayoutRtl());
+        setBasePadding(isCheckMarkAtStart());
     }
 
     @Override
@@ -306,7 +310,7 @@
         resetPaddingToInitialValues();
         int newPadding = (mCheckMarkDrawable != null) ?
                 mCheckMarkWidth + mBasePadding : mBasePadding;
-        if (isLayoutRtl()) {
+        if (isCheckMarkAtStart()) {
             mNeedRequestlayout |= (mPaddingLeft != newPadding);
             mPaddingLeft = newPadding;
         } else {
@@ -319,14 +323,20 @@
         }
     }
 
-    private void setBasePadding(boolean isLayoutRtl) {
-        if (isLayoutRtl) {
+    private void setBasePadding(boolean checkmarkAtStart) {
+        if (checkmarkAtStart) {
             mBasePadding = mPaddingLeft;
         } else {
             mBasePadding = mPaddingRight;
         }
     }
 
+    private boolean isCheckMarkAtStart() {
+        final int gravity = Gravity.getAbsoluteGravity(mCheckMarkGravity, getLayoutDirection());
+        final int hgrav = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+        return hgrav == Gravity.LEFT;
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         super.onDraw(canvas);
@@ -347,13 +357,13 @@
                     break;
             }
             
-            final boolean isLayoutRtl = isLayoutRtl();
+            final boolean checkMarkAtStart = isCheckMarkAtStart();
             final int width = getWidth();
             final int top = y;
             final int bottom = top + height;
             final int left;
             final int right;
-            if (isLayoutRtl) {
+            if (checkMarkAtStart) {
                 left = mBasePadding;
                 right = left + mCheckMarkWidth;
             } else {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a536b2d..a82fa65 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -1320,8 +1320,8 @@
                 attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);
 
         boolean focusable = mMovement != null || getKeyListener() != null;
-        boolean clickable = focusable;
-        boolean longClickable = focusable;
+        boolean clickable = focusable || isClickable();
+        boolean longClickable = focusable || isLongClickable();
 
         n = a.getIndexCount();
         for (int i = 0; i < n; i++) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index ee0d14b..a745b20 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -45,6 +45,7 @@
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.LogWriter;
@@ -3744,6 +3745,7 @@
     }
 
     public void noteNetworkInterfaceTypeLocked(String iface, int networkType) {
+        if (TextUtils.isEmpty(iface)) return;
         if (ConnectivityManager.isNetworkTypeMobile(networkType)) {
             mMobileIfaces = includeInStringArray(mMobileIfaces, iface);
             if (DEBUG) Slog.d(TAG, "Note mobile iface " + iface + ": " + mMobileIfaces);
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
new file mode 100644
index 0000000..e3f229f
--- /dev/null
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.os;
+
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.util.Slog;
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Represents a connection to {@code installd}. Allows multiple connect and
+ * disconnect cycles.
+ *
+ * @hide for internal use only
+ */
+public class InstallerConnection {
+    private static final String TAG = "InstallerConnection";
+    private static final boolean LOCAL_DEBUG = false;
+
+    private InputStream mIn;
+    private OutputStream mOut;
+    private LocalSocket mSocket;
+
+    private final byte buf[] = new byte[1024];
+
+    public InstallerConnection() {
+    }
+
+    public synchronized String transact(String cmd) {
+        if (!connect()) {
+            Slog.e(TAG, "connection failed");
+            return "-1";
+        }
+
+        if (!writeCommand(cmd)) {
+            /*
+             * If installd died and restarted in the background (unlikely but
+             * possible) we'll fail on the next write (this one). Try to
+             * reconnect and write the command one more time before giving up.
+             */
+            Slog.e(TAG, "write command failed? reconnect!");
+            if (!connect() || !writeCommand(cmd)) {
+                return "-1";
+            }
+        }
+        if (LOCAL_DEBUG) {
+            Slog.i(TAG, "send: '" + cmd + "'");
+        }
+
+        final int replyLength = readReply();
+        if (replyLength > 0) {
+            String s = new String(buf, 0, replyLength);
+            if (LOCAL_DEBUG) {
+                Slog.i(TAG, "recv: '" + s + "'");
+            }
+            return s;
+        } else {
+            if (LOCAL_DEBUG) {
+                Slog.i(TAG, "fail");
+            }
+            return "-1";
+        }
+    }
+
+    public int execute(String cmd) {
+        String res = transact(cmd);
+        try {
+            return Integer.parseInt(res);
+        } catch (NumberFormatException ex) {
+            return -1;
+        }
+    }
+
+    public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) {
+        StringBuilder builder = new StringBuilder("dexopt");
+        builder.append(' ');
+        builder.append(apkPath);
+        builder.append(' ');
+        builder.append(uid);
+        builder.append(isPublic ? " 1" : " 0");
+        builder.append(" *");         // No pkgName arg present
+        builder.append(' ');
+        builder.append(instructionSet);
+        return execute(builder.toString());
+    }
+
+    public int patchoat(String apkPath, int uid, boolean isPublic, String instructionSet) {
+        StringBuilder builder = new StringBuilder("patchoat");
+        builder.append(' ');
+        builder.append(apkPath);
+        builder.append(' ');
+        builder.append(uid);
+        builder.append(isPublic ? " 1" : " 0");
+        builder.append(" *");         // No pkgName arg present
+        builder.append(' ');
+        builder.append(instructionSet);
+        return execute(builder.toString());
+    }
+
+    private boolean connect() {
+        if (mSocket != null) {
+            return true;
+        }
+        Slog.i(TAG, "connecting...");
+        try {
+            mSocket = new LocalSocket();
+
+            LocalSocketAddress address = new LocalSocketAddress("installd",
+                    LocalSocketAddress.Namespace.RESERVED);
+
+            mSocket.connect(address);
+
+            mIn = mSocket.getInputStream();
+            mOut = mSocket.getOutputStream();
+        } catch (IOException ex) {
+            disconnect();
+            return false;
+        }
+        return true;
+    }
+
+    public void disconnect() {
+        Slog.i(TAG, "disconnecting...");
+        IoUtils.closeQuietly(mSocket);
+        IoUtils.closeQuietly(mIn);
+        IoUtils.closeQuietly(mOut);
+
+        mSocket = null;
+        mIn = null;
+        mOut = null;
+    }
+
+
+    private boolean readFully(byte[] buffer, int len) {
+        try {
+            Streams.readFully(mIn, buffer, 0, len);
+        } catch (IOException ioe) {
+            Slog.e(TAG, "read exception");
+            disconnect();
+            return false;
+        }
+
+        if (LOCAL_DEBUG) {
+            Slog.i(TAG, "read " + len + " bytes");
+        }
+
+        return true;
+    }
+
+    private int readReply() {
+        if (!readFully(buf, 2)) {
+            return -1;
+        }
+
+        final int len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
+        if ((len < 1) || (len > buf.length)) {
+            Slog.e(TAG, "invalid reply length (" + len + ")");
+            disconnect();
+            return -1;
+        }
+
+        if (!readFully(buf, len)) {
+            return -1;
+        }
+
+        return len;
+    }
+
+    private boolean writeCommand(String cmdString) {
+        final byte[] cmd = cmdString.getBytes();
+        final int len = cmd.length;
+        if ((len < 1) || (len > buf.length)) {
+            return false;
+        }
+
+        buf[0] = (byte) (len & 0xff);
+        buf[1] = (byte) ((len >> 8) & 0xff);
+        try {
+            mOut.write(buf, 0, 2);
+            mOut.write(cmd, 0, len);
+        } catch (IOException ex) {
+            Slog.e(TAG, "write error");
+            disconnect();
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 4a26b4b..d35fce4 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -192,13 +192,14 @@
      *
      * @param className Fully-qualified class name
      * @param argv Argument vector for main()
+     * @param classLoader the classLoader to load {@className} with
      */
-    private static void invokeStaticMain(String className, String[] argv)
+    private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
             throws ZygoteInit.MethodAndArgsCaller {
         Class<?> cl;
 
         try {
-            cl = Class.forName(className);
+            cl = Class.forName(className, true, classLoader);
         } catch (ClassNotFoundException ex) {
             throw new RuntimeException(
                     "Missing class when invoking static main " + className,
@@ -263,7 +264,7 @@
      * @param targetSdkVersion target SDK version
      * @param argv arg strings
      */
-    public static final void zygoteInit(int targetSdkVersion, String[] argv)
+    public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
             throws ZygoteInit.MethodAndArgsCaller {
         if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
 
@@ -272,7 +273,7 @@
         commonInit();
         nativeZygoteInit();
 
-        applicationInit(targetSdkVersion, argv);
+        applicationInit(targetSdkVersion, argv, classLoader);
     }
 
     /**
@@ -290,10 +291,10 @@
             throws ZygoteInit.MethodAndArgsCaller {
         if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper");
 
-        applicationInit(targetSdkVersion, argv);
+        applicationInit(targetSdkVersion, argv, null);
     }
 
-    private static void applicationInit(int targetSdkVersion, String[] argv)
+    private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
             throws ZygoteInit.MethodAndArgsCaller {
         // If the application calls System.exit(), terminate the process
         // immediately without running any shutdown hooks.  It is not possible to
@@ -317,7 +318,7 @@
         }
 
         // Remaining arguments are passed to the start class's static main
-        invokeStaticMain(args.startClass, args.startArgs);
+        invokeStaticMain(args.startClass, args.startArgs, classLoader);
     }
 
     /**
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 0c48368..43ebb3d 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -863,7 +863,7 @@
                         pipeFd, parsedArgs.remainingArgs);
             } else {
                 RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
-                        parsedArgs.remainingArgs);
+                        parsedArgs.remainingArgs, null /* classLoader */);
             }
         } else {
             String className;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index eea4201..051de6e 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -34,8 +34,11 @@
 import android.system.OsConstants;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.Slog;
 import android.webkit.WebViewFactory;
 
+import dalvik.system.DexFile;
+import dalvik.system.PathClassLoader;
 import dalvik.system.VMRuntime;
 
 import libcore.io.IoUtils;
@@ -493,21 +496,69 @@
             Process.setArgV0(parsedArgs.niceName);
         }
 
+        final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
+        if (systemServerClasspath != null) {
+            performSystemServerDexOpt(systemServerClasspath);
+        }
+
         if (parsedArgs.invokeWith != null) {
+            String[] args = parsedArgs.remainingArgs;
+            // If we have a non-null system server class path, we'll have to duplicate the
+            // existing arguments and append the classpath to it. ART will handle the classpath
+            // correctly when we exec a new process.
+            if (systemServerClasspath != null) {
+                String[] amendedArgs = new String[args.length + 2];
+                amendedArgs[0] = "-cp";
+                amendedArgs[1] = systemServerClasspath;
+                System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
+            }
+
             WrapperInit.execApplication(parsedArgs.invokeWith,
                     parsedArgs.niceName, parsedArgs.targetSdkVersion,
-                    null, parsedArgs.remainingArgs);
+                    null, args);
         } else {
+            ClassLoader cl = null;
+            if (systemServerClasspath != null) {
+                cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
+                Thread.currentThread().setContextClassLoader(cl);
+            }
+
             /*
              * Pass the remaining arguments to SystemServer.
              */
-            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs);
+            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
         }
 
         /* should never reach here */
     }
 
     /**
+     * Performs dex-opt on the elements of {@code classPath}, if needed. We
+     * choose the instruction set of the current runtime.
+     */
+    private static void performSystemServerDexOpt(String classPath) {
+        final String[] classPathElements = classPath.split(":");
+        final InstallerConnection installer = new InstallerConnection();
+        final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
+
+        try {
+            for (String classPathElement : classPathElements) {
+                final byte dexopt = DexFile.isDexOptNeededInternal(classPathElement, "*", instructionSet,
+                        false /* defer */);
+                if (dexopt == DexFile.DEXOPT_NEEDED) {
+                    installer.dexopt(classPathElement, Process.SYSTEM_UID, false, instructionSet);
+                } else if (dexopt == DexFile.PATCHOAT_NEEDED) {
+                    installer.patchoat(classPathElement, Process.SYSTEM_UID, false, instructionSet);
+                }
+            }
+        } catch (IOException ioe) {
+            throw new RuntimeException("Error starting system_server", ioe);
+        } finally {
+            installer.disconnect();
+        }
+    }
+
+    /**
      * Prepare the arguments and fork for the system server process.
      */
     private static boolean startSystemServer(String abiList, String socketName)
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index ea36e37..5709f659 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -184,6 +184,8 @@
                 Build.VERSION_CODES.KITKAT;
 
         mFlingEstimator = new OverScroller(context);
+
+        setFocusableInTouchMode(true);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
similarity index 100%
rename from services/core/java/com/android/server/BootReceiver.java
rename to core/java/com/android/server/BootReceiver.java
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 480383b..2106d38 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -246,6 +246,7 @@
 	libminikin \
 	libstlport \
 	libprocessgroup \
+	libnativebridge \
 
 ifeq ($(USE_OPENGL_RENDERER),true)
 	LOCAL_SHARED_LIBRARIES += libhwui
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 90c66d7..79b8542 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -161,10 +161,8 @@
 extern int register_android_text_StaticLayout(JNIEnv *env);
 extern int register_android_text_AndroidBidi(JNIEnv *env);
 extern int register_android_opengl_classes(JNIEnv *env);
-extern int register_android_server_fingerprint_FingerprintService(JNIEnv* env);
-extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env);
-extern int register_android_server_Watchdog(JNIEnv* env);
 extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
+extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env);
 extern int register_com_android_internal_os_ZygoteInit(JNIEnv* env);
 extern int register_android_backup_BackupDataInput(JNIEnv *env);
 extern int register_android_backup_BackupDataOutput(JNIEnv *env);
@@ -804,7 +802,7 @@
                                    "-Xmx", "-Xcompiler-option");
         if (skip_compilation) {
             addOption("-Xcompiler-option");
-            addOption("--compiler-filter=interpret-only");
+            addOption("--compiler-filter=verify-none");
         } else {
             parseCompilerOption("dalvik.vm.dex2oat-filter", dex2oatCompilerFilterBuf,
                                 "--compiler-filter=", "-Xcompiler-option");
@@ -1338,9 +1336,7 @@
     REG_JNI(register_android_media_ToneGenerator),
 
     REG_JNI(register_android_opengl_classes),
-	REG_JNI(register_android_server_fingerprint_FingerprintService),
     REG_JNI(register_android_server_NetworkManagementSocketTagger),
-    REG_JNI(register_android_server_Watchdog),
     REG_JNI(register_android_ddm_DdmHandleNativeHeap),
     REG_JNI(register_android_backup_BackupDataInput),
     REG_JNI(register_android_backup_BackupDataOutput),
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 9c44093..633a207 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -38,6 +38,8 @@
 #include "android_view_InputChannel.h"
 #include "android_view_KeyEvent.h"
 
+#include "nativebridge/native_bridge.h"
+
 #define LOG_TRACE(...)
 //#define LOG_TRACE(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
 
@@ -251,17 +253,29 @@
 
     const char* pathStr = env->GetStringUTFChars(path, NULL);
     NativeCode* code = NULL;
-    
+    bool needNativeBridge = false;
+
     void* handle = dlopen(pathStr, RTLD_LAZY);
-    
+    if (handle == NULL) {
+        if (NativeBridgeIsSupported(pathStr)) {
+            handle = NativeBridgeLoadLibrary(pathStr, RTLD_LAZY);
+            needNativeBridge = true;
+        }
+    }
     env->ReleaseStringUTFChars(path, pathStr);
-    
+
     if (handle != NULL) {
+        void* funcPtr = NULL;
         const char* funcStr = env->GetStringUTFChars(funcName, NULL);
-        code = new NativeCode(handle, (ANativeActivity_createFunc*)
-                dlsym(handle, funcStr));
+        if (needNativeBridge) {
+            funcPtr = NativeBridgeGetTrampoline(handle, funcStr, NULL, 0);
+        } else {
+            funcPtr = dlsym(handle, funcStr);
+        }
+
+        code = new NativeCode(handle, (ANativeActivity_createFunc*)funcPtr);
         env->ReleaseStringUTFChars(funcName, funcStr);
-        
+
         if (code->createActivityFunc == NULL) {
             ALOGW("ANativeActivity_onCreate not found");
             delete code;
diff --git a/core/res/res/color/primary_text_leanback_formwizard_dark.xml b/core/res/res/color/primary_text_leanback_formwizard_dark.xml
new file mode 100644
index 0000000..8fe02b2
--- /dev/null
+++ b/core/res/res/color/primary_text_leanback_formwizard_dark.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+        android:alpha="@dimen/disabled_alpha_leanback_formwizard"
+        android:color="@color/primary_text_leanback_formwizard_default_dark"/>
+    <item android:color="@color/primary_text_leanback_formwizard_default_dark"/>
+</selector>
diff --git a/core/res/res/layout/preference_widget_switch.xml b/core/res/res/layout/preference_widget_switch.xml
index 7395ff2..25e8aa6 100644
--- a/core/res/res/layout/preference_widget_switch.xml
+++ b/core/res/res/layout/preference_widget_switch.xml
@@ -21,4 +21,5 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:focusable="false"
+    android:clickable="false"
     android:background="@null" />
diff --git a/core/res/res/layout/select_dialog_multichoice_material.xml b/core/res/res/layout/select_dialog_multichoice_material.xml
index 8b4c59d..01e4cfa 100644
--- a/core/res/res/layout/select_dialog_multichoice_material.xml
+++ b/core/res/res/layout/select_dialog_multichoice_material.xml
@@ -26,4 +26,5 @@
     android:paddingStart="16dip"
     android:paddingEnd="16dip"
     android:checkMark="?android:attr/listChoiceIndicatorMultiple"
+    android:checkMarkGravity="start"
     android:ellipsize="marquee" />
diff --git a/core/res/res/layout/select_dialog_singlechoice_material.xml b/core/res/res/layout/select_dialog_singlechoice_material.xml
index 27a6648..0f3c277 100644
--- a/core/res/res/layout/select_dialog_singlechoice_material.xml
+++ b/core/res/res/layout/select_dialog_singlechoice_material.xml
@@ -26,4 +26,5 @@
     android:paddingStart="16dip"
     android:paddingEnd="16dip"
     android:checkMark="?android:attr/listChoiceIndicatorSingle"
+    android:checkMarkGravity="start"
     android:ellipsize="marquee" />
diff --git a/core/res/res/values-mcc206-mnc05/config.xml b/core/res/res/values-mcc206-mnc05/config.xml
new file mode 100644
index 0000000..a684aaa
--- /dev/null
+++ b/core/res/res/values-mcc206-mnc05/config.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>20610</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc206-mnc10/config.xml b/core/res/res/values-mcc206-mnc10/config.xml
new file mode 100644
index 0000000..5c96317
--- /dev/null
+++ b/core/res/res/values-mcc206-mnc10/config.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>20605</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc208-mnc10/config.xml b/core/res/res/values-mcc208-mnc10/config.xml
index 358bef6..10a6c5d 100644
--- a/core/res/res/values-mcc208-mnc10/config.xml
+++ b/core/res/res/values-mcc208-mnc10/config.xml
@@ -35,4 +35,28 @@
          note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
     <string translatable="false" name="config_tether_apndata">SFR option modem,websfr,,,,,,,,,208,10,,DUN</string>
 
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21420</item>
+        <item>21421</item>
+    </string-array>
+
 </resources>
diff --git a/core/res/res/values-mcc208-mnc15/config.xml b/core/res/res/values-mcc208-mnc15/config.xml
new file mode 100644
index 0000000..32b951c
--- /dev/null
+++ b/core/res/res/values-mcc208-mnc15/config.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>20801</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc01/config.xml b/core/res/res/values-mcc214-mnc01/config.xml
index 1b7c462..ecd8124 100644
--- a/core/res/res/values-mcc214-mnc01/config.xml
+++ b/core/res/res/values-mcc214-mnc01/config.xml
@@ -37,4 +37,27 @@
          note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
     <string translatable="false" name="config_tether_apndata">INTERNET,airtelnet.es,,,vodafone,vodafone,,,,,214,01,1,DUN</string>
 
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21420</item>
+        <item>21421</item>
+    </string-array>
+
 </resources>
diff --git a/core/res/res/values-mcc214-mnc02/config.xml b/core/res/res/values-mcc214-mnc02/config.xml
new file mode 100755
index 0000000..c83de57
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc02/config.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+
+<resources>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21420</item>
+        <item>21421</item>
+    </string-array>
+
+</resources>
diff --git a/core/res/res/values-mcc214-mnc04/config.xml b/core/res/res/values-mcc214-mnc04/config.xml
index 71301d5..6dfa87b 100644
--- a/core/res/res/values-mcc214-mnc04/config.xml
+++ b/core/res/res/values-mcc214-mnc04/config.xml
@@ -20,6 +20,25 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Don't use roaming icon for considered operators -->
     <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21405</item>
+        <item>21406</item>
         <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21420</item>
+        <item>21421</item>
     </string-array>
 </resources>
diff --git a/core/res/res/values-mcc214-mnc05/config.xml b/core/res/res/values-mcc214-mnc05/config.xml
new file mode 100755
index 0000000..9302b0c
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc05/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21420</item>
+        <item>21421</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc06/config.xml b/core/res/res/values-mcc214-mnc06/config.xml
new file mode 100755
index 0000000..c3f2643
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc06/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21420</item>
+        <item>21421</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc08/config.xml b/core/res/res/values-mcc214-mnc08/config.xml
new file mode 100755
index 0000000..5af6d5d
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc08/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21420</item>
+        <item>21421</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc09/config.xml b/core/res/res/values-mcc214-mnc09/config.xml
new file mode 100755
index 0000000..d789771
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc09/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21420</item>
+        <item>21421</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc10/config.xml b/core/res/res/values-mcc214-mnc10/config.xml
new file mode 100755
index 0000000..b66e1a2
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc10/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21420</item>
+        <item>21421</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc11/config.xml b/core/res/res/values-mcc214-mnc11/config.xml
new file mode 100755
index 0000000..9fd06db
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc11/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21420</item>
+        <item>21421</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc12/config.xml b/core/res/res/values-mcc214-mnc12/config.xml
new file mode 100755
index 0000000..7468238
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc12/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21420</item>
+        <item>21421</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc13/config.xml b/core/res/res/values-mcc214-mnc13/config.xml
new file mode 100755
index 0000000..35ff4ae
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc13/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21420</item>
+        <item>21421</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc14/config.xml b/core/res/res/values-mcc214-mnc14/config.xml
new file mode 100755
index 0000000..b6a7440
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc14/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21420</item>
+        <item>21421</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc15/config.xml b/core/res/res/values-mcc214-mnc15/config.xml
new file mode 100755
index 0000000..8296410
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc15/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21420</item>
+        <item>21421</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc16/config.xml b/core/res/res/values-mcc214-mnc16/config.xml
new file mode 100755
index 0000000..1aaf577
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc16/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21420</item>
+        <item>21421</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc17/config.xml b/core/res/res/values-mcc214-mnc17/config.xml
new file mode 100755
index 0000000..be92a32
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc17/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21420</item>
+        <item>21421</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc18/config.xml b/core/res/res/values-mcc214-mnc18/config.xml
new file mode 100755
index 0000000..078d7e2
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc18/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21419</item>
+        <item>21420</item>
+        <item>21421</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc19/config.xml b/core/res/res/values-mcc214-mnc19/config.xml
new file mode 100755
index 0000000..d194687
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc19/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21420</item>
+        <item>21421</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc20/config.xml b/core/res/res/values-mcc214-mnc20/config.xml
new file mode 100755
index 0000000..6aaf970
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc20/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21421</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc21/config.xml b/core/res/res/values-mcc214-mnc21/config.xml
new file mode 100755
index 0000000..f890b14
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc21/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21420</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc222-mnc10/config.xml b/core/res/res/values-mcc222-mnc10/config.xml
index 24dd71c..67467a0 100644
--- a/core/res/res/values-mcc222-mnc10/config.xml
+++ b/core/res/res/values-mcc222-mnc10/config.xml
@@ -35,4 +35,29 @@
          "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
          note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
     <string translatable="false" name="config_tether_apndata">Tethering Internet,web.omnitel.it,,,,,,,,,222,10,,DUN</string>
+
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>21401</item>
+        <item>21402</item>
+        <item>21403</item>
+        <item>21404</item>
+        <item>21405</item>
+        <item>21406</item>
+        <item>21407</item>
+        <item>21408</item>
+        <item>21409</item>
+        <item>21410</item>
+        <item>21411</item>
+        <item>21412</item>
+        <item>21413</item>
+        <item>21414</item>
+        <item>21415</item>
+        <item>21416</item>
+        <item>21417</item>
+        <item>21418</item>
+        <item>21419</item>
+        <item>21420</item>
+        <item>21421</item>
+    </string-array>
+
 </resources>
diff --git a/core/res/res/values-mcc234-mnc10/config.xml b/core/res/res/values-mcc234-mnc10/config.xml
new file mode 100644
index 0000000..b704d3f
--- /dev/null
+++ b/core/res/res/values-mcc234-mnc10/config.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>23420</item>
+        <item>23426</item>
+        <item>23430</item>
+        <item>23431</item>
+        <item>23432</item>
+        <item>23433</item>
+        <item>23434</item>
+        <item>23486</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc234-mnc20/config.xml b/core/res/res/values-mcc234-mnc20/config.xml
index d602c9f..1ed53dc 100644
--- a/core/res/res/values-mcc234-mnc20/config.xml
+++ b/core/res/res/values-mcc234-mnc20/config.xml
@@ -24,5 +24,15 @@
     <!-- Configure mobile network MTU. Carrier specific value is set here.
     -->
     <integer name="config_mobile_mtu">1440</integer>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>23410</item>
+        <item>23426</item>
+        <item>23430</item>
+        <item>23431</item>
+        <item>23432</item>
+        <item>23433</item>
+        <item>23434</item>
+        <item>23486</item>
+    </string-array>
 
 </resources>
diff --git a/core/res/res/values-mcc234-mnc26/config.xml b/core/res/res/values-mcc234-mnc26/config.xml
new file mode 100644
index 0000000..8d259de
--- /dev/null
+++ b/core/res/res/values-mcc234-mnc26/config.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>23410</item>
+        <item>23420</item>
+        <item>23430</item>
+        <item>23431</item>
+        <item>23432</item>
+        <item>23433</item>
+        <item>23434</item>
+        <item>23486</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc302-mnc220/config.xml b/core/res/res/values-mcc302-mnc220/config.xml
new file mode 100644
index 0000000..5259152
--- /dev/null
+++ b/core/res/res/values-mcc302-mnc220/config.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>302370</item>
+        <item>302610</item>
+        <item>302660</item>
+        <item>302720</item>
+        <item>302780</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc302-mnc370/config.xml b/core/res/res/values-mcc302-mnc370/config.xml
index 4fb2232..9cfe31f 100644
--- a/core/res/res/values-mcc302-mnc370/config.xml
+++ b/core/res/res/values-mcc302-mnc370/config.xml
@@ -40,4 +40,12 @@
     -->
     <integer name="config_mobile_mtu">1410</integer>
 
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>302220</item>
+        <item>302610</item>
+        <item>302660</item>
+        <item>302720</item>
+        <item>302780</item>
+    </string-array>
+
 </resources>
diff --git a/core/res/res/values-mcc302-mnc660/config.xml b/core/res/res/values-mcc302-mnc660/config.xml
index 76f7968..000f897 100644
--- a/core/res/res/values-mcc302-mnc660/config.xml
+++ b/core/res/res/values-mcc302-mnc660/config.xml
@@ -40,4 +40,12 @@
     -->
     <integer name="config_mobile_mtu">1430</integer>
 
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>302220</item>
+        <item>302370</item>
+        <item>302610</item>
+        <item>302720</item>
+        <item>302780</item>
+    </string-array>
+
 </resources>
diff --git a/core/res/res/values-mcc302-mnc720/config.xml b/core/res/res/values-mcc302-mnc720/config.xml
index 4eceffc..dfc58fe 100644
--- a/core/res/res/values-mcc302-mnc720/config.xml
+++ b/core/res/res/values-mcc302-mnc720/config.xml
@@ -40,4 +40,12 @@
     -->
     <integer name="config_mobile_mtu">1430</integer>
 
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>302220</item>
+        <item>302370</item>
+        <item>302610</item>
+        <item>302660</item>
+        <item>302780</item>
+    </string-array>
+
 </resources>
diff --git a/core/res/res/values-mcc310-mnc150/config.xml b/core/res/res/values-mcc310-mnc150/config.xml
new file mode 100644
index 0000000..f1936f4
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc150/config.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Show roaming icon though same named operators. -->
+    <string-array translatable="false" name="config_sameNamedOperatorConsideredRoaming">
+        <item>310110</item>
+        <item>310140</item>
+        <item>310400</item>
+        <item>310470</item>
+    </string-array>
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>311</item>
+        <item>312</item>
+        <item>313</item>
+        <item>314</item>
+        <item>315</item>
+        <item>316</item>
+    </string-array>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc170 b/core/res/res/values-mcc310-mnc170
new file mode 120000
index 0000000..cfced17
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc170
@@ -0,0 +1 @@
+./values-mcc310-mnc150
\ No newline at end of file
diff --git a/core/res/res/values-mcc310-mnc380 b/core/res/res/values-mcc310-mnc380
new file mode 120000
index 0000000..cfced17
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc380
@@ -0,0 +1 @@
+./values-mcc310-mnc150
\ No newline at end of file
diff --git a/core/res/res/values-mcc310-mnc410/config.xml b/core/res/res/values-mcc310-mnc410/config.xml
index 73aa1ce..c7ae8c8 100644
--- a/core/res/res/values-mcc310-mnc410/config.xml
+++ b/core/res/res/values-mcc310-mnc410/config.xml
@@ -24,5 +24,20 @@
     <!-- Configure mobile network MTU. Carrier specific value is set here.
     -->
     <integer name="config_mobile_mtu">1410</integer>
-
+    <!-- Show roaming icon though same named operators. -->
+    <string-array translatable="false" name="config_sameNamedOperatorConsideredRoaming">
+        <item>310110</item>
+        <item>310140</item>
+        <item>310400</item>
+        <item>310470</item>
+    </string-array>
+    <!-- BEGIN Motorola, xnk746, Feb-12-2014, IKVPREL2KK-1468 -->
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>311</item>
+        <item>312</item>
+        <item>313</item>
+        <item>314</item>
+        <item>315</item>
+        <item>316</item>
+    </string-array>
 </resources>
diff --git a/core/res/res/values-mcc340-mnc01/config.xml b/core/res/res/values-mcc340-mnc01/config.xml
index bbab4ad..bb491ed 100644
--- a/core/res/res/values-mcc340-mnc01/config.xml
+++ b/core/res/res/values-mcc340-mnc01/config.xml
@@ -35,4 +35,10 @@
          "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
          note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
     <string translatable="false" name="config_tether_apndata">Orangeweb,orangeweb,,,orange,orange,,,,,340,01,1,DUN</string>
+
+    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+        <item>20801</item>
+        <item>20815</item>
+    </string-array>
+
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 7f68492..89bda82 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3163,6 +3163,17 @@
                  result to valid color values. Saturate(S + D) -->
             <enum name="add" value="16" />
         </attr>
+        <!-- Gravity for aligning a CheckedTextView's checkmark to one side or the other. -->
+        <attr name="checkMarkGravity">
+            <!-- Push object to the left of its container, not changing its size. -->
+            <flag name="left" value="0x03" />
+            <!-- Push object to the right of its container, not changing its size. -->
+            <flag name="right" value="0x05" />
+            <!-- Push object to the beginning of its container, not changing its size. -->
+            <flag name="start" value="0x00800003" />
+            <!-- Push object to the end of its container, not changing its size. -->
+            <flag name="end" value="0x00800005" />
+        </attr>
     </declare-styleable>
     <declare-styleable name="EditText">
     </declare-styleable>
@@ -7276,9 +7287,8 @@
         <!-- Component name of an activity that allows the user to modify
              the settings for this service. -->
         <attr name="settingsActivity" />
-        <!-- Component name of an xml file that describes the structure of TV content ratings that
-             this service uses. -->
-        <attr name="contentRatingSystemXml" format="reference" />
+        <!-- Reference to an XML document that describes TV content rating. -->
+        <attr name="tvContentRatingDescription" format="reference" />
     </declare-styleable>
 
     <declare-styleable name="ResolverDrawerLayout">
diff --git a/core/res/res/values/colors_leanback.xml b/core/res/res/values/colors_leanback.xml
index dc0c673..e52a861e 100644
--- a/core/res/res/values/colors_leanback.xml
+++ b/core/res/res/values/colors_leanback.xml
@@ -25,4 +25,5 @@
     <color name="primary_text_leanback_light">#cc222222</color>
     <color name="secondary_text_leanback_light">#99222222</color>
 
+    <color name="primary_text_leanback_formwizard_default_dark">#ffeeeeee</color>
 </resources>
diff --git a/core/res/res/values/dimens_leanback.xml b/core/res/res/values/dimens_leanback.xml
new file mode 100644
index 0000000..fb5f8f0
--- /dev/null
+++ b/core/res/res/values/dimens_leanback.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+    <!-- Default alpha value for disabled elements. -->
+    <item name="disabled_alpha_leanback_formwizard" format="float" type="dimen">0.2</item>
+</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 60aacf2..e894a9c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2267,7 +2267,7 @@
   <public type="attr" name="windowReenterTransition" />
   <public type="attr" name="windowSharedElementReturnTransition" />
   <public type="attr" name="windowSharedElementReenterTransition" />
-  <public type="attr" name="contentRatingSystemXml"/>
+  <public type="attr" name="tvContentRatingDescription"/>
   <public type="attr" name="datePickerMode"/>
   <public type="attr" name="timePickerMode"/>
   <public type="attr" name="inset" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d74aaeb..8065a9c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4839,24 +4839,28 @@
     <string name="day_of_week_label_typeface">sans-serif</string>
 
     <!-- Notify use that they are in Lock-to-app -->
-    <string name="lock_to_app_toast">You are in lock-to-app mode. To exit, touch and hold the Recents button</string>
+    <string name="lock_to_app_toast">To unpin this screen, touch and hold  Back and Recents at the same time.</string>
+    <!-- Notify use that they are in Lock-to-app in accessibility mode -->
+    <string name="lock_to_app_toast_accessible">To unpin this screen, touch and hold Recents.</string>
     <!-- Notify user that they are locked in lock-to-app mode -->
-    <string name="lock_to_app_toast_locked">You are in Lock-to-App mode.</string>
+    <string name="lock_to_app_toast_locked">Screen is pinned. Unpinning isn\'t allowed by your organization.</string>
     <!-- Lock-to-app dialog title. -->
-    <string name="lock_to_app_title">Use lock-to-app?</string>
+    <string name="lock_to_app_title">Use screen pinning?</string>
     <!-- Lock-to-app dialog description. -->
-    <string name="lock_to_app_description">Lock-to-app locks the display in a single app.\n\nTo exit, touch and hold the Recents button.</string>
+    <string name="lock_to_app_description">Screen pinning locks the display in a single view.\n\nTo exit, touch and hold Back and Recents at the same time.</string>
+    <!-- Lock-to-app dialog description when in accessibility mode. -->
+    <string name="lock_to_app_description_accessible">Screen pinning locks the display in a single view.\n\nTo exit, touch and hold Recents.</string>
     <!-- Lock-to-app negative response. -->
     <string name="lock_to_app_negative">NO, THANKS</string>
     <!-- Lock-to-app positive response. -->
     <string name="lock_to_app_positive">START</string>
     <!-- Starting lock-to-app indication. -->
-    <string name="lock_to_app_start">Locked to app</string>
+    <string name="lock_to_app_start">Screen pinned</string>
     <!-- Exting lock-to-app indication. -->
-    <string name="lock_to_app_exit">No longer locked to app</string>
+    <string name="lock_to_app_exit">Screen unpinned</string>
 
     <!-- Lock-to-app checkbox for lock on exit -->
-    <string name="lock_to_app_use_screen_lock">Ask for %1$s before exiting</string>
+    <string name="lock_to_app_use_screen_lock">Ask for %1$s before unpinning</string>
 
     <!-- Lock-to-app unlock pin string -->
     <string name="lock_to_app_unlock_pin">PIN</string>
@@ -4867,35 +4871,35 @@
 
     <!-- Range specific TV content rating system strings for AM TV -->
     <string name="display_name_amtvrs" translatable="false">AM-TV-RS</string>
-    <string name="description_amtvrs">Range specific TV content rating system strings for Armenia</string>
+    <string name="description_amtvrs" translatable="false">Range specific TV content rating system strings for Armenia</string>
     <string name="display_name_amtvrs_y" translatable="false">Y</string>
     <string name="display_name_amtvrs_y7" translatable="false">Y7</string>
     <string name="display_name_amtvrs_ga" translatable="false">GA</string>
     <string name="display_name_amtvrs_tw" translatable="false">TW</string>
     <string name="display_name_amtvrs_t" translatable="false">T</string>
     <string name="display_name_amtvrs_a" translatable="false">A</string>
-    <string name="description_amtvrs_y">Suitable for ages 2-11</string>
-    <string name="description_amtvrs_y7">Suitable for ages 7-16</string>
-    <string name="description_amtvrs_ga">Suitable for general audiences</string>
-    <string name="description_amtvrs_tw">Suitable for teens ages 9 and up</string>
-    <string name="description_amtvrs_t">Suitable for teens ages 12 and up</string>
-    <string name="description_amtvrs_a">Suitable only for adults ages 18 and up</string>
+    <string name="description_amtvrs_y" translatable="false">Suitable for ages 2-11</string>
+    <string name="description_amtvrs_y7" translatable="false">Suitable for ages 7-16</string>
+    <string name="description_amtvrs_ga" translatable="false">Suitable for general audiences</string>
+    <string name="description_amtvrs_tw" translatable="false">Suitable for teens ages 9 and up</string>
+    <string name="description_amtvrs_t" translatable="false">Suitable for teens ages 12 and up</string>
+    <string name="description_amtvrs_a" translatable="false">Suitable only for adults ages 18 and up</string>
 
     <!-- Age specific TV content rating system strings for AM TV -->
     <string name="display_name_amtvas" translatable="false">AM-TV-AS</string>
-    <string name="description_amtvas">Age specific TV content rating system strings for Armenia</string>
+    <string name="description_amtvas" translatable="false">Age specific TV content rating system strings for Armenia</string>
     <string name="display_name_amtvas_ec" translatable="false">EC</string>
     <string name="display_name_amtvas_e" translatable="false">E</string>
     <string name="display_name_amtvas_e9" translatable="false">E9</string>
     <string name="display_name_amtvas_t" translatable="false">T</string>
     <string name="display_name_amtvas_m" translatable="false">M</string>
     <string name="display_name_amtvas_ao" translatable="false">AO</string>
-    <string name="description_amtvas_ec">Suitable for ages 2 and up</string>
-    <string name="description_amtvas_e">Suitable for ages 5 and up</string>
-    <string name="description_amtvas_e9">Suitable for ages 9 and up</string>
-    <string name="description_amtvas_t">Suitable for ages 12 and up</string>
-    <string name="description_amtvas_m">Suitable for ages 16 and up</string>
-    <string name="description_amtvas_ao">Suitable for ages 17 and up</string>
+    <string name="description_amtvas_ec" translatable="false">Suitable for ages 2 and up</string>
+    <string name="description_amtvas_e" translatable="false">Suitable for ages 5 and up</string>
+    <string name="description_amtvas_e9" translatable="false">Suitable for ages 9 and up</string>
+    <string name="description_amtvas_t" translatable="false">Suitable for ages 12 and up</string>
+    <string name="description_amtvas_m" translatable="false">Suitable for ages 16 and up</string>
+    <string name="description_amtvas_ao" translatable="false">Suitable for ages 17 and up</string>
 
     <!-- TV content rating system strings for AR TV -->
     <string name="display_name_artv" translatable="false">AR-TV</string>
@@ -4903,10 +4907,10 @@
     <string name="display_name_artv_13" translatable="false">Apto para mayores de 13 años</string>
     <string name="display_name_artv_16" translatable="false">Apto para mayores de 16 años</string>
     <string name="display_name_artv_18" translatable="false">Apto para mayores de 18 años</string>
-    <string name="description_artv_all">Suitable for all audiences. Programs may contain mild violence, language and mature situations</string>
-    <string name="description_artv_13">Suitable for ages 13 and up. Programs may contain mild to moderate language and mild violence and sexual references</string>
-    <string name="description_artv_16">Suitable for ages 16 and up. Programs may contain more intensive violence and coarse language, partial nudity and moderate sexual references</string>
-    <string name="description_artv_18">Suitable for mature audiences only. Programs contain strong violence, coarse language and explicit sexual references</string>
+    <string name="description_artv_all" translatable="false">Suitable for all audiences. Programs may contain mild violence, language and mature situations</string>
+    <string name="description_artv_13" translatable="false">Suitable for ages 13 and up. Programs may contain mild to moderate language and mild violence and sexual references</string>
+    <string name="description_artv_16" translatable="false">Suitable for ages 16 and up. Programs may contain more intensive violence and coarse language, partial nudity and moderate sexual references</string>
+    <string name="description_artv_18" translatable="false">Suitable for mature audiences only. Programs contain strong violence, coarse language and explicit sexual references</string>
 
     <!-- TV content rating system strings for AU TV -->
     <string name="display_name_autv" translatable="false">Australian TV Classification</string>
@@ -4917,13 +4921,13 @@
     <string name="display_name_autv_ma15" translatable="false">MA 15+</string>
     <string name="display_name_autv_r18" translatable="false">R 18+</string>
     <string name="display_name_autv_x18" translatable="false">X 18+</string>
-    <string name="description_autv_ctc">This has advertising approval, but is not yet classified</string>
-    <string name="description_autv_g">The content is very mild in impact, and suitable for everyone</string>
-    <string name="description_autv_pg">The content is mild in impact, but it may contain content that children find confusing or upsetting and may require the guidance or parents and guardians</string>
-    <string name="description_autv_m">The content is moderate in impact, and it is recommended for teenagers aged 15 years and over</string>
-    <string name="description_autv_ma15">The content is strong in impact, and it is legally restricted to persons 15 years and over</string>
-    <string name="description_autv_r18">The content is high in impact, and it is restricted to adults</string>
-    <string name="description_autv_x18">The content is restricted to adults. This classification is a special and legally restricted category which contains only sexually explicit content</string>
+    <string name="description_autv_ctc" translatable="false">This has advertising approval, but is not yet classified</string>
+    <string name="description_autv_g" translatable="false">The content is very mild in impact, and suitable for everyone</string>
+    <string name="description_autv_pg" translatable="false">The content is mild in impact, but it may contain content that children find confusing or upsetting and may require the guidance or parents and guardians</string>
+    <string name="description_autv_m" translatable="false">The content is moderate in impact, and it is recommended for teenagers aged 15 years and over</string>
+    <string name="description_autv_ma15" translatable="false">The content is strong in impact, and it is legally restricted to persons 15 years and over</string>
+    <string name="description_autv_r18" translatable="false">The content is high in impact, and it is restricted to adults</string>
+    <string name="description_autv_x18" translatable="false">The content is restricted to adults. This classification is a special and legally restricted category which contains only sexually explicit content</string>
 
     <!-- TV content rating system strings for BG TV -->
     <string name="display_name_bgtv" translatable="false">BG-TV</string>
@@ -4932,11 +4936,11 @@
     <string name="display_name_bgtv_c" translatable="false">C</string>
     <string name="display_name_bgtv_d" translatable="false">D</string>
     <string name="display_name_bgtv_x" translatable="false">X</string>
-    <string name="description_bgtv_a">Recommended to children. When the film confirms the ideals of humanism or popularizes the national and world cultures or contributes to upbringing children</string>
-    <string name="description_bgtv_b">No restrictive recommendations from the Committee. When the film is in no way contrary to the universal rules of morality in this country, has no restrictive recommendations from the Committee and does not fall in rating A</string>
-    <string name="description_bgtv_c">No persons under the age of 12 are admitted unless accompanied by an adult. When the film contains certain erotic scenes or scenes with drinking, taking drugs or stimulants or a few scenes of violence</string>
-    <string name="description_bgtv_d">No persons under the age of 16 are admitted. When the film contains quite a number of erotic scenes or scenes with drinking, taking drugs or stimulants or a considerable number of scenes showing violence</string>
-    <string name="description_bgtv_x">No persons under the age of 18 are admitted. When the film is naturalistically erotic or shows violence in an ostentatious manner</string>
+    <string name="description_bgtv_a" translatable="false">Recommended to children. When the film confirms the ideals of humanism or popularizes the national and world cultures or contributes to upbringing children</string>
+    <string name="description_bgtv_b" translatable="false">No restrictive recommendations from the Committee. When the film is in no way contrary to the universal rules of morality in this country, has no restrictive recommendations from the Committee and does not fall in rating A</string>
+    <string name="description_bgtv_c" translatable="false">No persons under the age of 12 are admitted unless accompanied by an adult. When the film contains certain erotic scenes or scenes with drinking, taking drugs or stimulants or a few scenes of violence</string>
+    <string name="description_bgtv_d" translatable="false">No persons under the age of 16 are admitted. When the film contains quite a number of erotic scenes or scenes with drinking, taking drugs or stimulants or a considerable number of scenes showing violence</string>
+    <string name="description_bgtv_x" translatable="false">No persons under the age of 18 are admitted. When the film is naturalistically erotic or shows violence in an ostentatious manner</string>
 
     <!-- TV content rating system strings for BR TV -->
     <string name="display_name_brtv" translatable="false">Brazil Content Rating</string>
@@ -4946,12 +4950,12 @@
     <string name="display_name_brtv_14" translatable="false">14 anos</string>
     <string name="display_name_brtv_16" translatable="false">16 anos</string>
     <string name="display_name_brtv_18" translatable="false">18 anos</string>
-    <string name="description_brtv_l">Content is suitable for all audiences</string>
-    <string name="description_brtv_10">Content suitable for viewers over the age of 10</string>
-    <string name="description_brtv_12">Content suitable for viewers over the age of 12</string>
-    <string name="description_brtv_14">Content suitable for viewers over the age of 14</string>
-    <string name="description_brtv_16">Content suitable for viewers over the age of 16</string>
-    <string name="description_brtv_18">Content suitable for viewers over the age of 18</string>
+    <string name="description_brtv_l" translatable="false">Content is suitable for all audiences</string>
+    <string name="description_brtv_10" translatable="false">Content suitable for viewers over the age of 10</string>
+    <string name="description_brtv_12" translatable="false">Content suitable for viewers over the age of 12</string>
+    <string name="description_brtv_14" translatable="false">Content suitable for viewers over the age of 14</string>
+    <string name="description_brtv_16" translatable="false">Content suitable for viewers over the age of 16</string>
+    <string name="description_brtv_18" translatable="false">Content suitable for viewers over the age of 18</string>
 
     <!-- TV content rating system strings for CA TV -->
     <string name="display_name_catv" translatable="false">Canadian TV Classification System</string>
@@ -4962,20 +4966,20 @@
     <string name="display_name_catv_pg" translatable="false">PG</string>
     <string name="display_name_catv_14" translatable="false">14+</string>
     <string name="display_name_catv_18" translatable="false">18+</string>
-    <string name="description_catv_exempt">Shows which are exempt from ratings (such as news and sports programming) will not display an on-screen rating at all</string>
-    <string name="description_catv_c">Programming suitable for children ages of 2-7 years. No profanity or sexual content of any level allowed. Contains little violence</string>
-    <string name="description_catv_c8">Suitable for children ages 8+. Low level violence and fantasy horror is allowed. No foul language is allowed, but occasional "socially offensive and discriminatory" language is allowed if in the context of the story. No sexual content of any level allowed</string>
-    <string name="description_catv_g">Suitable for general audiences. Programming suitable for the entire family with mild violence, and mild profanity and/or censored language</string>
-    <string name="description_catv_pg">Parental guidance. Moderate violence and moderate profanity is allowed, as is brief nudity and sexual references if important to the context of the story</string>
-    <string name="description_catv_14">Programming intended for viewers ages 14 and older. May contain strong violence and strong profanity, and depictions of sexual activity as long as they are within the context of a story</string>
-    <string name="description_catv_18">Programming intended for viewers ages 18 and older. May contain explicit violence and sexual activity</string>
+    <string name="description_catv_exempt" translatable="false">Shows which are exempt from ratings (such as news and sports programming) will not display an on-screen rating at all</string>
+    <string name="description_catv_c" translatable="false">Programming suitable for children ages of 2-7 years. No profanity or sexual content of any level allowed. Contains little violence</string>
+    <string name="description_catv_c8" translatable="false">Suitable for children ages 8+. Low level violence and fantasy horror is allowed. No foul language is allowed, but occasional "socially offensive and discriminatory" language is allowed if in the context of the story. No sexual content of any level allowed</string>
+    <string name="description_catv_g" translatable="false">Suitable for general audiences. Programming suitable for the entire family with mild violence, and mild profanity and/or censored language</string>
+    <string name="description_catv_pg" translatable="false">Parental guidance. Moderate violence and moderate profanity is allowed, as is brief nudity and sexual references if important to the context of the story</string>
+    <string name="description_catv_14" translatable="false">Programming intended for viewers ages 14 and older. May contain strong violence and strong profanity, and depictions of sexual activity as long as they are within the context of a story</string>
+    <string name="description_catv_18" translatable="false">Programming intended for viewers ages 18 and older. May contain explicit violence and sexual activity</string>
 
     <!-- TV content rating system strings for CH TV -->
     <string name="display_name_chtv" translatable="false">CH-TV</string>
     <string name="display_name_chtv_all" translatable="false">All ages</string>
     <string name="display_name_chtv_red" translatable="false">Red rectangle</string>
-    <string name="description_chtv_all">This program is suitable for all ages</string>
-    <string name="description_chtv_red">This program contains scenes that may hurt sensitive people, therefore the red symbol will be displayed</string>
+    <string name="description_chtv_all" translatable="false">This program is suitable for all ages</string>
+    <string name="description_chtv_red" translatable="false">This program contains scenes that may hurt sensitive people, therefore the red symbol will be displayed</string>
 
     <!-- TV content rating system strings for CL TV -->
     <string name="display_name_cltv" translatable="false">CL-TV</string>
@@ -4986,13 +4990,13 @@
     <string name="display_name_cltv_f" translatable="false">F</string>
     <string name="display_name_cltv_r" translatable="false">R</string>
     <string name="display_name_cltv_a" translatable="false">A</string>
-    <string name="description_cltv_i">Programs suitable for all children</string>
-    <string name="description_cltv_i7">Programs recommended for children ages 7 or older</string>
-    <string name="description_cltv_i10">Programs recommended for children ages 10 or older</string>
-    <string name="description_cltv_i12">Programs recommended for children and teens ages 12 or older</string>
-    <string name="description_cltv_f">Programs suitable for a general audience, with content appropriate for all ages</string>
-    <string name="description_cltv_r">Programs may content not suitable for children not accompanied by an adult</string>
-    <string name="description_cltv_a">Programs suitable for adult audiences only (ages 18 or older), may contain coarse language, and sexual or explicit situations</string>
+    <string name="description_cltv_i" translatable="false">Programs suitable for all children</string>
+    <string name="description_cltv_i7" translatable="false">Programs recommended for children ages 7 or older</string>
+    <string name="description_cltv_i10" translatable="false">Programs recommended for children ages 10 or older</string>
+    <string name="description_cltv_i12" translatable="false">Programs recommended for children and teens ages 12 or older</string>
+    <string name="description_cltv_f" translatable="false">Programs suitable for a general audience, with content appropriate for all ages</string>
+    <string name="description_cltv_r" translatable="false">Programs may content not suitable for children not accompanied by an adult</string>
+    <string name="description_cltv_a" translatable="false">Programs suitable for adult audiences only (ages 18 or older), may contain coarse language, and sexual or explicit situations</string>
 
     <!-- TV content rating system strings for DE TV -->
     <string name="display_name_detv" translatable="false">DE-TV</string>
@@ -5000,10 +5004,10 @@
     <string name="display_name_detv_12" translatable="false">ab 12 Jahren</string>
     <string name="display_name_detv_16" translatable="false">ab 16 Jahren</string>
     <string name="display_name_detv_18" translatable="false">ab 18 Jahren</string>
-    <string name="description_detv_all">The program is suitable for all ages</string>
-    <string name="description_detv_12">The program is not suitable for viewers under the age of 12</string>
-    <string name="description_detv_16">The program is not suitable for viewers under the age of 16</string>
-    <string name="description_detv_18">The program is not suitable for viewers under the age of 18</string>
+    <string name="description_detv_all" translatable="false">The program is suitable for all ages</string>
+    <string name="description_detv_12" translatable="false">The program is not suitable for viewers under the age of 12</string>
+    <string name="description_detv_16" translatable="false">The program is not suitable for viewers under the age of 16</string>
+    <string name="description_detv_18" translatable="false">The program is not suitable for viewers under the age of 18</string>
 
     <!-- TV content rating system strings for DK TV -->
     <string name="display_name_dktv" translatable="false">DK-TV</string>
@@ -5011,10 +5015,10 @@
     <string name="display_name_dktv_y" translatable="false">Yellow symbol</string>
     <string name="display_name_dktv_r" translatable="false">Red symbol</string>
     <string name="display_name_dktv_b" translatable="false">Blue symbol</string>
-    <string name="description_dktv_g">programs suitable for all ages</string>
-    <string name="description_dktv_y">programs suitable children accompanied by an adult</string>
-    <string name="description_dktv_r">programs containing material with more intensive content </string>
-    <string name="description_dktv_b">programs containing explicit content and strictly for adults only</string>
+    <string name="description_dktv_g" translatable="false">programs suitable for all ages</string>
+    <string name="description_dktv_y" translatable="false">programs suitable children accompanied by an adult</string>
+    <string name="description_dktv_r" translatable="false">programs containing material with more intensive content </string>
+    <string name="description_dktv_b" translatable="false">programs containing explicit content and strictly for adults only</string>
 
     <!-- TV content rating system strings for ES TV -->
     <string name="display_name_estv" translatable="false">ES-TV</string>
@@ -5025,13 +5029,13 @@
     <string name="display_name_estv_12" translatable="false">12</string>
     <string name="display_name_estv_16" translatable="false">16</string>
     <string name="display_name_estv_18" translatable="false">18</string>
-    <string name="description_estv_tp">Recommended for all ages</string>
-    <string name="description_estv_i">Specially recommended for preschoolers and kids</string>
-    <string name="description_estv_7">Recommended for people older than 7 years old</string>
-    <string name="description_estv_7i">Recommended for kids older than 7 years old</string>
-    <string name="description_estv_12">Recommended for people older than 12 years old</string>
-    <string name="description_estv_16">Recommended for people older than 16 years old</string>
-    <string name="description_estv_18">Recommended for people older than 18 years old</string>
+    <string name="description_estv_tp" translatable="false">Recommended for all ages</string>
+    <string name="description_estv_i" translatable="false">Specially recommended for preschoolers and kids</string>
+    <string name="description_estv_7" translatable="false">Recommended for people older than 7 years old</string>
+    <string name="description_estv_7i" translatable="false">Recommended for kids older than 7 years old</string>
+    <string name="description_estv_12" translatable="false">Recommended for people older than 12 years old</string>
+    <string name="description_estv_16" translatable="false">Recommended for people older than 16 years old</string>
+    <string name="description_estv_18" translatable="false">Recommended for people older than 18 years old</string>
 
     <!-- TV content rating system strings for FI TV -->
     <string name="display_name_fitv" translatable="false">FI-TV</string>
@@ -5040,11 +5044,11 @@
     <string name="display_name_fitv_k12" translatable="false">K12</string>
     <string name="display_name_fitv_k16" translatable="false">K16</string>
     <string name="display_name_fitv_k18" translatable="false">K18</string>
-    <string name="description_fitv_s">Allowed at all times</string>
-    <string name="description_fitv_k7">Not recommended for children under 7</string>
-    <string name="description_fitv_k12">Not recommended for children under 12</string>
-    <string name="description_fitv_k16">Not recommended for children under 16</string>
-    <string name="description_fitv_k18">Not recommended for children under 18</string>
+    <string name="description_fitv_s" translatable="false">Allowed at all times</string>
+    <string name="description_fitv_k7" translatable="false">Not recommended for children under 7</string>
+    <string name="description_fitv_k12" translatable="false">Not recommended for children under 12</string>
+    <string name="description_fitv_k16" translatable="false">Not recommended for children under 16</string>
+    <string name="description_fitv_k18" translatable="false">Not recommended for children under 18</string>
 
     <!-- TV content rating system strings for FR TV -->
     <string name="display_name_frtv" translatable="false">FR-TV</string>
@@ -5053,11 +5057,11 @@
     <string name="display_name_frtv_12" translatable="false">Déconseillé aux -12 ans</string>
     <string name="display_name_frtv_16" translatable="false">Déconseillé aux -16 ans</string>
     <string name="display_name_frtv_18" translatable="false">Déconseillé aux -18 ans</string>
-    <string name="description_frtv_all">Appropriate for all ages</string>
-    <string name="description_frtv_10">Not recommended for children under 10</string>
-    <string name="description_frtv_12">Not recommended for children under 12</string>
-    <string name="description_frtv_16">Not recommended for children under 16</string>
-    <string name="description_frtv_18">Not recommended for persons under 18</string>
+    <string name="description_frtv_all" translatable="false">Appropriate for all ages</string>
+    <string name="description_frtv_10" translatable="false">Not recommended for children under 10</string>
+    <string name="description_frtv_12" translatable="false">Not recommended for children under 12</string>
+    <string name="description_frtv_16" translatable="false">Not recommended for children under 16</string>
+    <string name="description_frtv_18" translatable="false">Not recommended for persons under 18</string>
 
     <!-- TV content rating system strings for GR TV -->
     <string name="display_name_grtv" translatable="false">GR-TV</string>
@@ -5066,20 +5070,20 @@
     <string name="display_name_grtv_12" translatable="false">White triangle in orange background</string>
     <string name="display_name_grtv_15" translatable="false">White square in purple background</string>
     <string name="display_name_grtv_18" translatable="false">White X in red background</string>
-    <string name="description_grtv_all">Suitable for all ages</string>
-    <string name="description_grtv_10">Parental consent suggested</string>
-    <string name="description_grtv_12">Required parental consent</string>
-    <string name="description_grtv_15">Suitable for minors over the age of 15</string>
-    <string name="description_grtv_18">Suitable only for adults profanity before midnight is punishable by fine, except when used in the context of the program</string>
+    <string name="description_grtv_all" translatable="false">Suitable for all ages</string>
+    <string name="description_grtv_10" translatable="false">Parental consent suggested</string>
+    <string name="description_grtv_12" translatable="false">Required parental consent</string>
+    <string name="description_grtv_15" translatable="false">Suitable for minors over the age of 15</string>
+    <string name="description_grtv_18" translatable="false">Suitable only for adults profanity before midnight is punishable by fine, except when used in the context of the program</string>
 
     <!-- TV content rating system strings for HK TV -->
     <string name="display_name_hktv" translatable="false">HK-TV</string>
     <string name="display_name_hktv_g" translatable="false">G</string>
     <string name="display_name_hktv_pg" translatable="false">PG</string>
     <string name="display_name_hktv_m" translatable="false">M</string>
-    <string name="description_hktv_g">For general audiences</string>
-    <string name="description_hktv_pg">Programs are unsuitable for children, parental guidance is recommended</string>
-    <string name="description_hktv_m">Programs are recommended only for adult viewers above the age of 18</string>
+    <string name="description_hktv_g" translatable="false">For general audiences</string>
+    <string name="description_hktv_pg" translatable="false">Programs are unsuitable for children, parental guidance is recommended</string>
+    <string name="description_hktv_m" translatable="false">Programs are recommended only for adult viewers above the age of 18</string>
 
     <!-- TV content rating system strings for HU TV -->
     <string name="display_name_hutv" translatable="false">HU-TV</string>
@@ -5089,12 +5093,12 @@
     <string name="display_name_hutv_12" translatable="false">12</string>
     <string name="display_name_hutv_16" translatable="false">16</string>
     <string name="display_name_hutv_18" translatable="false">18</string>
-    <string name="description_hutv_u">Programs can be viewed by any age</string>
-    <string name="description_hutv_cf">Programs recommended for children. It is an optional rating, there is no obligation for broadcasters to indicate it</string>
-    <string name="description_hutv_6">Programs not recommended for children below the age of 6, may not contain any violence or sexual content</string>
-    <string name="description_hutv_12">Programs not recommended for children below the age of 12, may contain light sexual content or explicit language</string>
-    <string name="description_hutv_16">Programs not recommended for teens and children below the age of 16, may contain more intensive violence and sexual content</string>
-    <string name="description_hutv_18">The program is recommended only for adult viewers (for ages 18 and up), may contain explicit violence and explicit sexual content</string>
+    <string name="description_hutv_u" translatable="false">Programs can be viewed by any age</string>
+    <string name="description_hutv_cf" translatable="false">Programs recommended for children. It is an optional rating, there is no obligation for broadcasters to indicate it</string>
+    <string name="description_hutv_6" translatable="false">Programs not recommended for children below the age of 6, may not contain any violence or sexual content</string>
+    <string name="description_hutv_12" translatable="false">Programs not recommended for children below the age of 12, may contain light sexual content or explicit language</string>
+    <string name="description_hutv_16" translatable="false">Programs not recommended for teens and children below the age of 16, may contain more intensive violence and sexual content</string>
+    <string name="description_hutv_18" translatable="false">The program is recommended only for adult viewers (for ages 18 and up), may contain explicit violence and explicit sexual content</string>
 
     <!-- TV content rating system strings for ID TV -->
     <string name="display_name_idtv" translatable="false">ID-TV</string>
@@ -5106,14 +5110,14 @@
     <string name="display_name_idtv_r" translatable="false">R</string>
     <string name="display_name_idtv_r_bo" translatable="false">R-BO</string>
     <string name="display_name_idtv_d" translatable="false">D</string>
-    <string name="description_idtv_p">Suitable for children from ages 2 through 11</string>
-    <string name="description_idtv_a">Suitable for teens and children from ages 7 through 16</string>
-    <string name="description_idtv_a_bo">Suitable for children ages 5 through 10, with parental guidance or permission</string>
-    <string name="description_idtv_su">Suitable for general audiences</string>
-    <string name="description_idtv_bo">Parental guidance suggested for ages 5 and under</string>
-    <string name="description_idtv_r">Suitable for teens from ages 13 through 17</string>
-    <string name="description_idtv_r_bo">Suitable for teens with parental guidance or permission</string>
-    <string name="description_idtv_d">Suitable for viewers over 18 and older only</string>
+    <string name="description_idtv_p" translatable="false">Suitable for children from ages 2 through 11</string>
+    <string name="description_idtv_a" translatable="false">Suitable for teens and children from ages 7 through 16</string>
+    <string name="description_idtv_a_bo" translatable="false">Suitable for children ages 5 through 10, with parental guidance or permission</string>
+    <string name="description_idtv_su" translatable="false">Suitable for general audiences</string>
+    <string name="description_idtv_bo" translatable="false">Parental guidance suggested for ages 5 and under</string>
+    <string name="description_idtv_r" translatable="false">Suitable for teens from ages 13 through 17</string>
+    <string name="description_idtv_r_bo" translatable="false">Suitable for teens with parental guidance or permission</string>
+    <string name="description_idtv_d" translatable="false">Suitable for viewers over 18 and older only</string>
 
     <!-- TV content rating system strings for IE TV -->
     <string name="display_name_ietv" translatable="false">RTÉ programme classifications</string>
@@ -5122,11 +5126,11 @@
     <string name="display_name_ietv_ya" translatable="false">YA</string>
     <string name="display_name_ietv_ps" translatable="false">PS</string>
     <string name="display_name_ietv_ma" translatable="false">MA</string>
-    <string name="description_ietv_ga">Suitable for all ages</string>
-    <string name="description_ietv_ch">Suitable for children ages 5 to 10, may contain comedic violence or action fantasy violence</string>
-    <string name="description_ietv_ya">Suitable for adolescent audiences, may contain thematic elements that would appeal to teenagers</string>
-    <string name="description_ietv_ps">Suitable for more mature viewers, more mature themes may be present</string>
-    <string name="description_ietv_ma">Most restrictive classification, allowing for heavy subject matter and coarse language</string>
+    <string name="description_ietv_ga" translatable="false">Suitable for all ages</string>
+    <string name="description_ietv_ch" translatable="false">Suitable for children ages 5 to 10, may contain comedic violence or action fantasy violence</string>
+    <string name="description_ietv_ya" translatable="false">Suitable for adolescent audiences, may contain thematic elements that would appeal to teenagers</string>
+    <string name="description_ietv_ps" translatable="false">Suitable for more mature viewers, more mature themes may be present</string>
+    <string name="description_ietv_ma" translatable="false">Most restrictive classification, allowing for heavy subject matter and coarse language</string>
 
     <!-- TV content rating system strings for IL TV -->
     <string name="display_name_iltv" translatable="false">IL-TV</string>
@@ -5135,11 +5139,11 @@
     <string name="display_name_iltv_15" translatable="false">15+</string>
     <string name="display_name_iltv_18" translatable="false">18+</string>
     <string name="display_name_iltv_e" translatable="false">E</string>
-    <string name="description_iltv_g">General audience; anyone, regardless of age, can view the program, usually news and children\'s programming</string>
-    <string name="description_iltv_12">Suitable for teens and children ages 12 and over, no child under 12 are permitted to view the program</string>
-    <string name="description_iltv_15">Suitable for teens ages 15 and over, no child under 15 may view the programme</string>
-    <string name="description_iltv_18">Suitable for adults only, no minors may view the programme</string>
-    <string name="description_iltv_e">Exempt from classification</string>
+    <string name="description_iltv_g" translatable="false">General audience; anyone, regardless of age, can view the program, usually news and children\'s programming</string>
+    <string name="description_iltv_12" translatable="false">Suitable for teens and children ages 12 and over, no child under 12 are permitted to view the program</string>
+    <string name="description_iltv_15" translatable="false">Suitable for teens ages 15 and over, no child under 15 may view the programme</string>
+    <string name="description_iltv_18" translatable="false">Suitable for adults only, no minors may view the programme</string>
+    <string name="description_iltv_e" translatable="false">Exempt from classification</string>
 
     <!-- TV content rating system strings for IN TV -->
     <string name="display_name_intv" translatable="false">IN-TV</string>
@@ -5147,10 +5151,10 @@
     <string name="display_name_intv_u/a" translatable="false">U/A</string>
     <string name="display_name_intv_a" translatable="false">A</string>
     <string name="display_name_intv_s" translatable="false">S</string>
-    <string name="description_intv_u">Unrestricted public exhibition</string>
-    <string name="description_intv_u/a">Unrestricted public exhibition, but with a caution regarding parental guidance to those under 12 years of age</string>
-    <string name="description_intv_a">Public exhibition restricted to adults 18 years of age and older only</string>
-    <string name="description_intv_s">Public exhibition restricted to members of any profession or any class of persons</string>
+    <string name="description_intv_u" translatable="false">Unrestricted public exhibition</string>
+    <string name="description_intv_u/a" translatable="false">Unrestricted public exhibition, but with a caution regarding parental guidance to those under 12 years of age</string>
+    <string name="description_intv_a" translatable="false">Public exhibition restricted to adults 18 years of age and older only</string>
+    <string name="description_intv_s" translatable="false">Public exhibition restricted to members of any profession or any class of persons</string>
 
     <!-- TV content rating system strings for IS TV -->
     <string name="display_name_istv" translatable="false">IS-TV</string>
@@ -5161,13 +5165,13 @@
     <string name="display_name_istv_14" translatable="false">14</string>
     <string name="display_name_istv_16" translatable="false">16</string>
     <string name="display_name_istv_18" translatable="false">18</string>
-    <string name="description_istv_l">Programs suitable for all ages</string>
-    <string name="description_istv_7">Programs suitable for ages 7 and older</string>
-    <string name="description_istv_10">Programs suitable for ages 10 and older</string>
-    <string name="description_istv_12">Programs suitable for ages 12 and older</string>
-    <string name="description_istv_14">Programs suitable for ages 14 and older</string>
-    <string name="description_istv_16">Programs suitable for ages 16 and older</string>
-    <string name="description_istv_18">Programs suitable for ages 18 and older</string>
+    <string name="description_istv_l" translatable="false">Programs suitable for all ages</string>
+    <string name="description_istv_7" translatable="false">Programs suitable for ages 7 and older</string>
+    <string name="description_istv_10" translatable="false">Programs suitable for ages 10 and older</string>
+    <string name="description_istv_12" translatable="false">Programs suitable for ages 12 and older</string>
+    <string name="description_istv_14" translatable="false">Programs suitable for ages 14 and older</string>
+    <string name="description_istv_16" translatable="false">Programs suitable for ages 16 and older</string>
+    <string name="description_istv_18" translatable="false">Programs suitable for ages 18 and older</string>
 
     <!-- TV content rating system strings for KR TV -->
     <string name="display_name_krtv" translatable="false">KR-TV</string>
@@ -5176,11 +5180,11 @@
     <string name="display_name_krtv_12" translatable="false">12세이상시청가</string>
     <string name="display_name_krtv_15" translatable="false">15세이상시청가</string>
     <string name="display_name_krtv_19" translatable="false">19세이상시청가</string>
-    <string name="description_krtv_all">Appropriate for all ages</string>
-    <string name="description_krtv_7">May contain material inappropriate for children younger than 7, and parental discretion should be used</string>
-    <string name="description_krtv_12">May deemed inappropriate for those younger than 12, and parental discretion should be used</string>
-    <string name="description_krtv_15">May be inappropriate for children under 15, and that parental discretion should be used</string>
-    <string name="description_krtv_19">For adults only</string>
+    <string name="description_krtv_all" translatable="false">Appropriate for all ages</string>
+    <string name="description_krtv_7" translatable="false">May contain material inappropriate for children younger than 7, and parental discretion should be used</string>
+    <string name="description_krtv_12" translatable="false">May deemed inappropriate for those younger than 12, and parental discretion should be used</string>
+    <string name="description_krtv_15" translatable="false">May be inappropriate for children under 15, and that parental discretion should be used</string>
+    <string name="description_krtv_19" translatable="false">For adults only</string>
 
     <!-- TV content rating system strings for MV TV -->
     <string name="display_name_mvtv" translatable="false">MV-TV</string>
@@ -5193,15 +5197,15 @@
     <string name="display_name_mvtv_18" translatable="false">18+</string>
     <string name="display_name_mvtv_21" translatable="false">21+</string>
     <string name="display_name_mvtv_x" translatable="false">X</string>
-    <string name="description_mvtv_y">Young children</string>
-    <string name="description_mvtv_g">General viewing for all ages</string>
-    <string name="description_mvtv_pg">Parental guidance is required unaccompanied children</string>
-    <string name="description_mvtv_pg-12">Parental guidance is required for children under the age of 12</string>
-    <string name="description_mvtv_12">Teens and children aged 12 and older may watch, otherwise restricted</string>
-    <string name="description_mvtv_15">Restricted to viewers aged 15 and above</string>
-    <string name="description_mvtv_18">Restricted to viewers aged 18 and above</string>
-    <string name="description_mvtv_21">Restricted to viewers aged 21 and above</string>
-    <string name="description_mvtv_x">Most restrictive classification, only adults ages 25 and above may view</string>
+    <string name="description_mvtv_y" translatable="false">Young children</string>
+    <string name="description_mvtv_g" translatable="false">General viewing for all ages</string>
+    <string name="description_mvtv_pg" translatable="false">Parental guidance is required unaccompanied children</string>
+    <string name="description_mvtv_pg-12" translatable="false">Parental guidance is required for children under the age of 12</string>
+    <string name="description_mvtv_12" translatable="false">Teens and children aged 12 and older may watch, otherwise restricted</string>
+    <string name="description_mvtv_15" translatable="false">Restricted to viewers aged 15 and above</string>
+    <string name="description_mvtv_18" translatable="false">Restricted to viewers aged 18 and above</string>
+    <string name="description_mvtv_21" translatable="false">Restricted to viewers aged 21 and above</string>
+    <string name="description_mvtv_x" translatable="false">Most restrictive classification, only adults ages 25 and above may view</string>
 
     <!-- TV content rating system strings for MX TV -->
     <string name="display_name_mxtv" translatable="false">MX-TV</string>
@@ -5211,21 +5215,21 @@
     <string name="display_name_mxtv_c" translatable="false">C</string>
     <string name="display_name_mxtv_d" translatable="false">D</string>
     <string name="display_name_mxtv_rc" translatable="false">RC</string>
-    <string name="description_mxtv_a">Appropriate for all ages, parental guidance is recommended for children under 7 years</string>
-    <string name="description_mxtv_b">Designed for ages 12 and older, may contain some sexual situations, mild violence, and mild language</string>
-    <string name="description_mxtv_b-15">Designed for ages 15 and up, slightly more intensive than the \'A\' and \'B\' ratings</string>
-    <string name="description_mxtv_c">Designed to be viewed by adults aged 18 or older only, generally more intensive content </string>
-    <string name="description_mxtv_d">Designed to be viewed only by mature adults (at least 21 years of age and over), contains extreme content matter</string>
-    <string name="description_mxtv_rc">Banned from public television in Mexico</string>
+    <string name="description_mxtv_a" translatable="false">Appropriate for all ages, parental guidance is recommended for children under 7 years</string>
+    <string name="description_mxtv_b" translatable="false">Designed for ages 12 and older, may contain some sexual situations, mild violence, and mild language</string>
+    <string name="description_mxtv_b-15" translatable="false">Designed for ages 15 and up, slightly more intensive than the \'A\' and \'B\' ratings</string>
+    <string name="description_mxtv_c" translatable="false">Designed to be viewed by adults aged 18 or older only, generally more intensive content </string>
+    <string name="description_mxtv_d" translatable="false">Designed to be viewed only by mature adults (at least 21 years of age and over), contains extreme content matter</string>
+    <string name="description_mxtv_rc" translatable="false">Banned from public television in Mexico</string>
 
     <!-- TV content rating system strings for MY TV -->
     <string name="display_name_mytv" translatable="false">MY-TV</string>
     <string name="display_name_mytv_u" translatable="false">U</string>
     <string name="display_name_mytv_p13" translatable="false">P13</string>
     <string name="display_name_mytv_18" translatable="false">18</string>
-    <string name="description_mytv_u">General viewing for all ages, can be broadcast anytime</string>
-    <string name="description_mytv_p13">For viewers ages 13 and above, children under 13 needs parental guidance, can be broadcast anytime, but some elements may only be broadcast at night</string>
-    <string name="description_mytv_18">For viewers ages 18 and above only</string>
+    <string name="description_mytv_u" translatable="false">General viewing for all ages, can be broadcast anytime</string>
+    <string name="description_mytv_p13" translatable="false">For viewers ages 13 and above, children under 13 needs parental guidance, can be broadcast anytime, but some elements may only be broadcast at night</string>
+    <string name="description_mytv_18" translatable="false">For viewers ages 18 and above only</string>
 
     <!-- TV content rating system strings for NL TV -->
     <string name="display_name_nltv" translatable="false">NICAM</string>
@@ -5240,31 +5244,31 @@
     <string name="display_name_nltv_9" translatable="false">Let op met kinderen tot 9 jaar</string>
     <string name="display_name_nltv_12" translatable="false">Let op met kinderen tot 12 jaar</string>
     <string name="display_name_nltv_16" translatable="false">Let op met kinderen tot 16 jaar</string>
-    <string name="description_nltv_v">Violence\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
-    <string name="description_nltv_f">Scary or Disturbing ContentViolence\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
-    <string name="description_nltv_s">Sexual Content\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
-    <string name="description_nltv_d">Discrimination\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
-    <string name="description_nltv_da">Drug and/or Alcohol abuse\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
-    <string name="description_nltv_l">Bad Language\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
-    <string name="description_nltv_al">All Ages</string>
-    <string name="description_nltv_6">Parental advisory for children under 6</string>
-    <string name="description_nltv_9">Parental advisory for children under 9</string>
-    <string name="description_nltv_12">Parental advisory for children under 12</string>
-    <string name="description_nltv_16">Parental advisory for children under 16</string>
+    <string name="description_nltv_v" translatable="false">Violence\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
+    <string name="description_nltv_f" translatable="false">Scary or Disturbing ContentViolence\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
+    <string name="description_nltv_s" translatable="false">Sexual Content\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
+    <string name="description_nltv_d" translatable="false">Discrimination\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
+    <string name="description_nltv_da" translatable="false">Drug and/or Alcohol abuse\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
+    <string name="description_nltv_l" translatable="false">Bad Language\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
+    <string name="description_nltv_al" translatable="false">All Ages</string>
+    <string name="description_nltv_6" translatable="false">Parental advisory for children under 6</string>
+    <string name="description_nltv_9" translatable="false">Parental advisory for children under 9</string>
+    <string name="description_nltv_12" translatable="false">Parental advisory for children under 12</string>
+    <string name="description_nltv_16" translatable="false">Parental advisory for children under 16</string>
 
     <!-- TV content rating system strings for NZF(Free) TV -->
     <string name="display_name_nzftv" translatable="false">NZ-Free-TV</string>
-    <string name="description_nzftv">TV content rating system for free-to-air channels in New Zealand</string>
+    <string name="description_nzftv" translatable="false">TV content rating system for free-to-air channels in New Zealand</string>
     <string name="display_name_nzftv_g" translatable="false">G</string>
     <string name="display_name_nzftv_pgr" translatable="false">PGR</string>
     <string name="display_name_nzftv_ao" translatable="false">AO</string>
-    <string name="description_nzftv_g">These exclude material likely to harm children under 14 and can screen at any time. Programmes may not necessarily be designed for younger viewers, but must not contain material likely to cause them undue distress or discomfort</string>
-    <string name="description_nzftv_pgr">Programmes more suited to more mature viewers. These are not necessarily unsuitable for children, but viewer discretion is advised, and parents and guardians are encouraged to supervise younger viewers</string>
-    <string name="description_nzftv_ao">Contain material of an adult nature handled in such a way that it is unsuitable for children</string>
+    <string name="description_nzftv_g" translatable="false">These exclude material likely to harm children under 14 and can screen at any time. Programmes may not necessarily be designed for younger viewers, but must not contain material likely to cause them undue distress or discomfort</string>
+    <string name="description_nzftv_pgr" translatable="false">Programmes more suited to more mature viewers. These are not necessarily unsuitable for children, but viewer discretion is advised, and parents and guardians are encouraged to supervise younger viewers</string>
+    <string name="description_nzftv_ao" translatable="false">Contain material of an adult nature handled in such a way that it is unsuitable for children</string>
 
     <!-- TV content rating system strings for NZP(Pay) TV -->
     <string name="display_name_nzptv" translatable="false">NZ-Pay-TV</string>
-    <string name="description_nzptv">TV content rating system for Pay TV channels in New Zealand</string>
+    <string name="description_nzptv" translatable="false">TV content rating system for Pay TV channels in New Zealand</string>
     <string name="display_name_nzptv_c" translatable="false">C</string>
     <string name="display_name_nzptv_v" translatable="false">V</string>
     <string name="display_name_nzptv_l" translatable="false">L</string>
@@ -5274,46 +5278,46 @@
     <string name="display_name_nzptv_m" translatable="false">M</string>
     <string name="display_name_nzptv_16" translatable="false">16</string>
     <string name="display_name_nzptv_18" translatable="false">18</string>
-    <string name="description_nzptv_c">Content may offend\nApplicable to NZ_PTV_PG, NZ_PTV_M, NZ_PTV_16, NZ_PTV_18</string>
-    <string name="description_nzptv_v">Violence\nApplicable to NZ_PTV_PG, NZ_PTV_M, NZ_PTV_16, NZ_PTV_18</string>
-    <string name="description_nzptv_l">Language\nApplicable to NZ_PTV_PG, NZ_PTV_M, NZ_PTV_16, NZ_PTV_18</string>
-    <string name="description_nzptv_s">Sexual content\nApplicable to NZ_PTV_PG, NZ_PTV_M, NZ_PTV_16, NZ_PTV_18</string>
-    <string name="description_nzptv_g">suitable for general audiences</string>
-    <string name="description_nzptv_pg">Parental guidance recommended for under 10</string>
-    <string name="description_nzptv_m">Suitable for mature audiences 13 and up</string>
-    <string name="description_nzptv_16">Suitable for viewers 16 and up</string>
-    <string name="description_nzptv_18">Suitable for viewers 18 and up</string>
+    <string name="description_nzptv_c" translatable="false">Content may offend\nApplicable to NZ_PTV_PG, NZ_PTV_M, NZ_PTV_16, NZ_PTV_18</string>
+    <string name="description_nzptv_v" translatable="false">Violence\nApplicable to NZ_PTV_PG, NZ_PTV_M, NZ_PTV_16, NZ_PTV_18</string>
+    <string name="description_nzptv_l" translatable="false">Language\nApplicable to NZ_PTV_PG, NZ_PTV_M, NZ_PTV_16, NZ_PTV_18</string>
+    <string name="description_nzptv_s" translatable="false">Sexual content\nApplicable to NZ_PTV_PG, NZ_PTV_M, NZ_PTV_16, NZ_PTV_18</string>
+    <string name="description_nzptv_g" translatable="false">suitable for general audiences</string>
+    <string name="description_nzptv_pg" translatable="false">Parental guidance recommended for under 10</string>
+    <string name="description_nzptv_m" translatable="false">Suitable for mature audiences 13 and up</string>
+    <string name="description_nzptv_16" translatable="false">Suitable for viewers 16 and up</string>
+    <string name="description_nzptv_18" translatable="false">Suitable for viewers 18 and up</string>
 
     <!-- TV content rating system strings for PE TV -->
     <string name="display_name_petv" translatable="false">PE-TV</string>
-    <string name="description_petv">TV content rating system for some Peruvian channels in Peru</string>
+    <string name="description_petv" translatable="false">TV content rating system for some Peruvian channels in Peru</string>
     <string name="display_name_petv_a" translatable="false">Apt</string>
     <string name="display_name_petv_14" translatable="false">14</string>
     <string name="display_name_petv_18" translatable="false">18</string>
-    <string name="description_petv_a">Suitable for all audiences</string>
-    <string name="description_petv_14">Suitable for people aged 14 and above only</string>
-    <string name="description_petv_18">Suitable for people aged 18 and above only</string>
+    <string name="description_petv_a" translatable="false">Suitable for all audiences</string>
+    <string name="description_petv_14" translatable="false">Suitable for people aged 14 and above only</string>
+    <string name="description_petv_18" translatable="false">Suitable for people aged 18 and above only</string>
 
     <!-- TV content rating system strings for America TV in PE -->
     <string name="display_name_peatv" translatable="false">PE-ATV</string>
-    <string name="description_peatv">TV content rating system for America Television in Peru that uses its own rating system</string>
+    <string name="description_peatv" translatable="false">TV content rating system for America Television in Peru that uses its own rating system</string>
     <string name="display_name_peatv_gp" translatable="false">GP</string>
     <string name="display_name_peatv_pg" translatable="false">PG</string>
     <string name="display_name_peatv_14" translatable="false">TV-14</string>
     <string name="display_name_peatv_18" translatable="false">TV-18</string>
-    <string name="description_peatv_gp">General audience</string>
-    <string name="description_peatv_pg">Parental guidance required for under 6</string>
-    <string name="description_peatv_14">Suitable for people aged 14 and above only</string>
-    <string name="description_peatv_18">Suitable for people aged 18 and above only</string>
+    <string name="description_peatv_gp" translatable="false">General audience</string>
+    <string name="description_peatv_pg" translatable="false">Parental guidance required for under 6</string>
+    <string name="description_peatv_14" translatable="false">Suitable for people aged 14 and above only</string>
+    <string name="description_peatv_18" translatable="false">Suitable for people aged 18 and above only</string>
 
     <!-- TV content rating system strings for PH TV -->
     <string name="display_name_phtv" translatable="false">MTRCB</string>
     <string name="display_name_phtv_g" translatable="false">G</string>
     <string name="display_name_phtv_pg" translatable="false">PG</string>
     <string name="display_name_phtv_spg" translatable="false">SPG</string>
-    <string name="description_phtv_g">Suitable for all public viewers</string>
-    <string name="description_phtv_pg">Programmes rated PG may contain scenes or other content that are unsuitable for children without the guidance of a parent</string>
-    <string name="description_phtv_spg">Contains mature themes or moderate to intense violence, which may be deemed unfit for children to watch without strict parental supervision</string>
+    <string name="description_phtv_g" translatable="false">Suitable for all public viewers</string>
+    <string name="description_phtv_pg" translatable="false">Programmes rated PG may contain scenes or other content that are unsuitable for children without the guidance of a parent</string>
+    <string name="description_phtv_spg" translatable="false">Contains mature themes or moderate to intense violence, which may be deemed unfit for children to watch without strict parental supervision</string>
 
     <!-- TV content rating system strings for PL TV -->
     <string name="display_name_pltv" translatable="false">PL-TV</string>
@@ -5322,11 +5326,11 @@
     <string name="display_name_pltv_12" translatable="false">12</string>
     <string name="display_name_pltv_16" translatable="false">16</string>
     <string name="display_name_pltv_18" translatable="false">18</string>
-    <string name="description_pltv_g">Positive or neutral view of the world, little to no violence, non-sexual love, and no sexual content</string>
-    <string name="description_pltv_7">Age 7 and above. May additionally contain some mild language, bloodless violence, and a more negative view of the world</string>
-    <string name="description_pltv_12">Age 12 and above. May contain some foul language, some violence, and some sexual content</string>
-    <string name="description_pltv_16">Age 16 and above. Deviant social behaviour, world filled with violence and sexuality, simplified picture of adulthood, display of physical force, especially in controversial social context, immoral behaviour without ethic dilemma, putting the blame on the victim, excessive concentration on material possessions</string>
-    <string name="description_pltv_18">Age 18 and above. One-sided display of the joys of adult life without showing responsibilities, social justification of violent behaviour, excessive vulgarity, use of racial slurs and social stereotypes, explicit sexual content, praise of aggression or vulgarity</string>
+    <string name="description_pltv_g" translatable="false">Positive or neutral view of the world, little to no violence, non-sexual love, and no sexual content</string>
+    <string name="description_pltv_7" translatable="false">Age 7 and above. May additionally contain some mild language, bloodless violence, and a more negative view of the world</string>
+    <string name="description_pltv_12" translatable="false">Age 12 and above. May contain some foul language, some violence, and some sexual content</string>
+    <string name="description_pltv_16" translatable="false">Age 16 and above. Deviant social behaviour, world filled with violence and sexuality, simplified picture of adulthood, display of physical force, especially in controversial social context, immoral behaviour without ethic dilemma, putting the blame on the victim, excessive concentration on material possessions</string>
+    <string name="description_pltv_18" translatable="false">Age 18 and above. One-sided display of the joys of adult life without showing responsibilities, social justification of violent behaviour, excessive vulgarity, use of racial slurs and social stereotypes, explicit sexual content, praise of aggression or vulgarity</string>
 
     <!-- TV content rating system strings for PT TV -->
     <string name="display_name_pttv" translatable="false">PT-TV</string>
@@ -5334,10 +5338,10 @@
     <string name="display_name_pttv_10" translatable="false">10</string>
     <string name="display_name_pttv_12" translatable="false">12</string>
     <string name="display_name_pttv_16" translatable="false">16</string>
-    <string name="description_pttv_t">Suitable for all</string>
-    <string name="description_pttv_10">May not be suitable for children under 10, parental guidance advised</string>
-    <string name="description_pttv_12">May not be suitable for children under 12, parental guidance advised</string>
-    <string name="description_pttv_16">Not suitable for children under 16</string>
+    <string name="description_pttv_t" translatable="false">Suitable for all</string>
+    <string name="description_pttv_10" translatable="false">May not be suitable for children under 10, parental guidance advised</string>
+    <string name="description_pttv_12" translatable="false">May not be suitable for children under 12, parental guidance advised</string>
+    <string name="description_pttv_16" translatable="false">Not suitable for children under 16</string>
 
     <!-- TV content rating system strings for RO TV -->
     <string name="display_name_rotv" translatable="false">RO-TV</string>
@@ -5347,12 +5351,12 @@
     <string name="display_name_rotv_12" translatable="false">12</string>
     <string name="display_name_rotv_15" translatable="false">15</string>
     <string name="display_name_rotv_18" translatable="false">18</string>
-    <string name="description_rotv_y">Young Ages</string>
-    <string name="description_rotv_g">General Exhibition</string>
-    <string name="description_rotv_ap">Parental guidance is recommended for children below the age of 12</string>
-    <string name="description_rotv_12">Forbidden for children under 12 years of age</string>
-    <string name="description_rotv_15">Forbidden for children under 15 years of age</string>
-    <string name="description_rotv_18">Forbidden for children under 18 years of age</string>
+    <string name="description_rotv_y" translatable="false">Young Ages</string>
+    <string name="description_rotv_g" translatable="false">General Exhibition</string>
+    <string name="description_rotv_ap" translatable="false">Parental guidance is recommended for children below the age of 12</string>
+    <string name="description_rotv_12" translatable="false">Forbidden for children under 12 years of age</string>
+    <string name="description_rotv_15" translatable="false">Forbidden for children under 15 years of age</string>
+    <string name="description_rotv_18" translatable="false">Forbidden for children under 18 years of age</string>
 
     <!-- TV content rating system strings for RU TV -->
     <string name="display_name_rutv" translatable="false">RU-TV</string>
@@ -5361,11 +5365,11 @@
     <string name="display_name_rutv_12" translatable="false">12+</string>
     <string name="display_name_rutv_16" translatable="false">16+</string>
     <string name="display_name_rutv_18" translatable="false">18+</string>
-    <string name="description_rutv_0">Can be watched by Any Age</string>
-    <string name="description_rutv_6">Only kids the age of 6 or older can watch</string>
-    <string name="description_rutv_12">Only kids the age of 12 or older can watch</string>
-    <string name="description_rutv_16">Only teens the age of 16 or older can watch</string>
-    <string name="description_rutv_18">Restricted to children ONLY people 18 or older</string>
+    <string name="description_rutv_0" translatable="false">Can be watched by Any Age</string>
+    <string name="description_rutv_6" translatable="false">Only kids the age of 6 or older can watch</string>
+    <string name="description_rutv_12" translatable="false">Only kids the age of 12 or older can watch</string>
+    <string name="description_rutv_16" translatable="false">Only teens the age of 16 or older can watch</string>
+    <string name="description_rutv_18" translatable="false">Restricted to children ONLY people 18 or older</string>
 
     <!-- TV content rating system strings for RS TV -->
     <string name="display_name_rstv" translatable="false">RS-TV</string>
@@ -5376,27 +5380,27 @@
     <string name="display_name_rstv_16" translatable="false">16</string>
     <string name="display_name_rstv_17" translatable="false">17</string>
     <string name="display_name_rstv_18" translatable="false">18</string>
-    <string name="description_rstv_g">Program suitable for all ages</string>
-    <string name="description_rstv_12">Program not suitable for children under the age of 12 </string>
-    <string name="description_rstv_14">Program not suitable for children/teens under the age of 14</string>
-    <string name="description_rstv_15">Program not suitable for children/teens under the age of 15</string>
-    <string name="description_rstv_16">Program not suitable for children/teens under the age of 16</string>
-    <string name="description_rstv_17">Program not suitable for children/teens under the age of 17</string>
-    <string name="description_rstv_18">Program not suitable for minors under the age of 18</string>
+    <string name="description_rstv_g" translatable="false">Program suitable for all ages</string>
+    <string name="description_rstv_12" translatable="false">Program not suitable for children under the age of 12 </string>
+    <string name="description_rstv_14" translatable="false">Program not suitable for children/teens under the age of 14</string>
+    <string name="description_rstv_15" translatable="false">Program not suitable for children/teens under the age of 15</string>
+    <string name="description_rstv_16" translatable="false">Program not suitable for children/teens under the age of 16</string>
+    <string name="description_rstv_17" translatable="false">Program not suitable for children/teens under the age of 17</string>
+    <string name="description_rstv_18" translatable="false">Program not suitable for minors under the age of 18</string>
 
     <!-- TV content rating system strings for SGF(Free-to-Air) TV -->
     <string name="display_name_sgftv" translatable="false">SG-Free-TV</string>
     <string name="display_name_sgftv_pg" translatable="false">PG</string>
     <string name="display_name_sgftv_pg13" translatable="false">PG13</string>
-    <string name="description_sgftv_pg">Suitable for most but parents should guide their young</string>
-    <string name="description_sgftv_pg13">Parental Guidance Strongly Cautioned - Suitable for 13 And Above</string>
+    <string name="description_sgftv_pg" translatable="false">Suitable for most but parents should guide their young</string>
+    <string name="description_sgftv_pg13" translatable="false">Parental Guidance Strongly Cautioned - Suitable for 13 And Above</string>
 
     <!-- TV content rating system strings for SGP(Pay TV) TV -->
     <string name="display_name_sgptv" translatable="false">SG-Pay-TV</string>
     <string name="display_name_sgptv_nc16" translatable="false">NC16</string>
     <string name="display_name_sgptv_m18" translatable="false">M18</string>
-    <string name="description_sgptv_nc16">No Children Under 16</string>
-    <string name="description_sgptv_m18">Nobody under age 18 is admitted</string>
+    <string name="description_sgptv_nc16" translatable="false">No Children Under 16</string>
+    <string name="description_sgptv_m18" translatable="false">Nobody under age 18 is admitted</string>
 
     <!-- TV content rating system strings for SI TV -->
     <string name="display_name_sitv" translatable="false">SI-TV</string>
@@ -5404,10 +5408,10 @@
     <string name="display_name_sitv_12" translatable="false">+12</string>
     <string name="display_name_sitv_15" translatable="false">+15</string>
     <string name="display_name_sitv_ad" translatable="false">AD</string>
-    <string name="description_sitv_vs">Parental guidance suggested (for children under 6)</string>
-    <string name="description_sitv_12">Content suitable for teens over 12 years</string>
-    <string name="description_sitv_15">Content suitable for teens over 15 years</string>
-    <string name="description_sitv_ad">Content exclusively for adults</string>
+    <string name="description_sitv_vs" translatable="false">Parental guidance suggested (for children under 6)</string>
+    <string name="description_sitv_12" translatable="false">Content suitable for teens over 12 years</string>
+    <string name="description_sitv_15" translatable="false">Content suitable for teens over 15 years</string>
+    <string name="description_sitv_ad" translatable="false">Content exclusively for adults</string>
 
     <!-- TV content rating system strings for TH TV -->
     <string name="display_name_thtv" translatable="false">TH-TV</string>
@@ -5417,12 +5421,12 @@
     <string name="display_name_thtv_pg13" translatable="false">PG13</string>
     <string name="display_name_thtv_pg18" translatable="false">PG18</string>
     <string name="display_name_thtv_adults" translatable="false">Adults</string>
-    <string name="description_thtv_primary">Content suitable for primary school aged children</string>
-    <string name="description_thtv_children">Content suitable for children between 6-12 years old</string>
-    <string name="description_thtv_general">Content suitable for general audiences</string>
-    <string name="description_thtv_pg13">Content suitable for people aged 13 and above, but can be watched by those who are under the recommended age if parental guidance is provided</string>
-    <string name="description_thtv_pg18">Content suitable for people aged above 18 years old; those who are younger that 18 must be provided with parental guidance</string>
-    <string name="description_thtv_adults">Content unsuitable for children and youngsters</string>
+    <string name="description_thtv_primary" translatable="false">Content suitable for primary school aged children</string>
+    <string name="description_thtv_children" translatable="false">Content suitable for children between 6-12 years old</string>
+    <string name="description_thtv_general" translatable="false">Content suitable for general audiences</string>
+    <string name="description_thtv_pg13" translatable="false">Content suitable for people aged 13 and above, but can be watched by those who are under the recommended age if parental guidance is provided</string>
+    <string name="description_thtv_pg18" translatable="false">Content suitable for people aged above 18 years old; those who are younger that 18 must be provided with parental guidance</string>
+    <string name="description_thtv_adults" translatable="false">Content unsuitable for children and youngsters</string>
 
     <!-- TV content rating system strings for TR TV -->
     <string name="display_name_trtv" translatable="false">TR-TV</string>
@@ -5430,10 +5434,10 @@
     <string name="display_name_trtv_7" translatable="false">7+</string>
     <string name="display_name_trtv_13" translatable="false">13+</string>
     <string name="display_name_trtv_18" translatable="false">18+</string>
-    <string name="description_trtv_g">General audience. Suitable for all ages</string>
-    <string name="description_trtv_7">Suitable for ages 7 and over</string>
-    <string name="description_trtv_13">Suitable for ages 13 and over</string>
-    <string name="description_trtv_18">Suitable for ages 13 and over</string>
+    <string name="description_trtv_g" translatable="false">General audience. Suitable for all ages</string>
+    <string name="description_trtv_7" translatable="false">Suitable for ages 7 and over</string>
+    <string name="description_trtv_13" translatable="false">Suitable for ages 13 and over</string>
+    <string name="description_trtv_18" translatable="false">Suitable for ages 13 and over</string>
 
     <!-- TV content rating system strings for TW TV -->
     <string name="display_name_twtv" translatable="false">TW-TV</string>
@@ -5441,23 +5445,23 @@
     <string name="display_name_twtv_p" translatable="false">Protected category</string>
     <string name="display_name_twtv_pg" translatable="false">Parental guidance category</string>
     <string name="display_name_twtv_r" translatable="false">Restricted category</string>
-    <string name="description_twtv_g">For all ages</string>
-    <string name="description_twtv_p">Not suitable for children under 6 years old. People aged 6 but under 12 require guidance from accompanying adults to watch</string>
-    <string name="description_twtv_pg">Not suitable for people under 12 years of age. Parental guidance is required for people aged 12 but under 18</string>
-    <string name="description_twtv_r">For adults only and people under 18 years of age must not watch</string>
+    <string name="description_twtv_g" translatable="false">For all ages</string>
+    <string name="description_twtv_p" translatable="false">Not suitable for children under 6 years old. People aged 6 but under 12 require guidance from accompanying adults to watch</string>
+    <string name="description_twtv_pg" translatable="false">Not suitable for people under 12 years of age. Parental guidance is required for people aged 12 but under 18</string>
+    <string name="description_twtv_r" translatable="false">For adults only and people under 18 years of age must not watch</string>
 
     <!-- TV content rating system strings for UA TV -->
     <string name="display_name_uatv" translatable="false">UA-TV</string>
     <string name="display_name_uatv_green circle" translatable="false">Green Circle</string>
     <string name="display_name_uatv_yellow triangle" translatable="false">Yellow Triangle</string>
     <string name="display_name_uatv_red square" translatable="false">Red Square</string>
-    <string name="description_uatv_green circle">This program does not have age restrictions</string>
-    <string name="description_uatv_yellow triangle">Children must view this program with parents. In it program there are fragments, which unsuitable for children</string>
-    <string name="description_uatv_red square">This program is only for adult viewers. In it there are scenes with nudity, drug use, or violence</string>
+    <string name="description_uatv_green circle" translatable="false">This program does not have age restrictions</string>
+    <string name="description_uatv_yellow triangle" translatable="false">Children must view this program with parents. In it program there are fragments, which unsuitable for children</string>
+    <string name="description_uatv_red square" translatable="false">This program is only for adult viewers. In it there are scenes with nudity, drug use, or violence</string>
 
     <!-- TV content rating system strings for US TV -->
     <string name="display_name_ustv" translatable="false">US-TV</string>
-    <string name="description_ustv">TV content rating system for United States</string>
+    <string name="description_ustv" translatable="false">TV content rating system for United States</string>
     <string name="display_name_ustv_d" translatable="false">D</string>
     <string name="display_name_ustv_l" translatable="false">L</string>
     <string name="display_name_ustv_s" translatable="false">S</string>
@@ -5469,26 +5473,26 @@
     <string name="display_name_ustv_pg" translatable="false">TV-PG</string>
     <string name="display_name_ustv_14" translatable="false">TV-14</string>
     <string name="display_name_ustv_ma" translatable="false">TV-MA</string>
-    <string name="description_ustv_d">Suggestive dialogue (Usually means talks about sex)\nApplicable to US_TV_PG, US_TV_14, US_TV</string>
-    <string name="description_ustv_l">Coarse language\nApplicable to US_TV_PG, US_TV_14</string>
-    <string name="description_ustv_s">Sexual content\nApplicable to US_TV_PG, US_TV_14, US_TV_MA</string>
-    <string name="description_ustv_v">Violence\nApplicable to US_TV_PG, US_TV_14, US_TV_MA</string>
-    <string name="description_ustv_fv">Fantasy violence (Children\'s programming only)\nApplicable to US_TV_Y7</string>
-    <string name="description_ustv_y">This program is designed to be appropriate for all children</string>
-    <string name="description_ustv_y7">This program is designed for children age 7 and above</string>
-    <string name="description_ustv_g">Most parents would find this program suitable for all ages</string>
-    <string name="description_ustv_pg">This program contains material that parents may find unsuitable for younger children</string>
-    <string name="description_ustv_14">This program contains some material that many parents would find unsuitable for children under 14 years of age</string>
-    <string name="description_ustv_ma">This program is specifically designed to be viewed by adults and therefore may be unsuitable for children under 17</string>
+    <string name="description_ustv_d" translatable="false">Suggestive dialogue (Usually means talks about sex)\nApplicable to US_TV_PG, US_TV_14, US_TV</string>
+    <string name="description_ustv_l" translatable="false">Coarse language\nApplicable to US_TV_PG, US_TV_14</string>
+    <string name="description_ustv_s" translatable="false">Sexual content\nApplicable to US_TV_PG, US_TV_14, US_TV_MA</string>
+    <string name="description_ustv_v" translatable="false">Violence\nApplicable to US_TV_PG, US_TV_14, US_TV_MA</string>
+    <string name="description_ustv_fv" translatable="false">Fantasy violence (Children\'s programming only)\nApplicable to US_TV_Y7</string>
+    <string name="description_ustv_y" translatable="false">This program is designed to be appropriate for all children</string>
+    <string name="description_ustv_y7" translatable="false">This program is designed for children age 7 and above</string>
+    <string name="description_ustv_g" translatable="false">Most parents would find this program suitable for all ages</string>
+    <string name="description_ustv_pg" translatable="false">This program contains material that parents may find unsuitable for younger children</string>
+    <string name="description_ustv_14" translatable="false">This program contains some material that many parents would find unsuitable for children under 14 years of age</string>
+    <string name="description_ustv_ma" translatable="false">This program is specifically designed to be viewed by adults and therefore may be unsuitable for children under 17</string>
 
     <!-- TV content rating system strings for VE TV -->
     <string name="display_name_vetv" translatable="false">VE-TV</string>
     <string name="display_name_vetv_tu" translatable="false">TU</string>
     <string name="display_name_vetv_su" translatable="false">SU</string>
     <string name="display_name_vetv_a" translatable="false">A</string>
-    <string name="description_vetv_tu">For all ages</string>
-    <string name="description_vetv_su">Parental guidance for young viewers</string>
-    <string name="description_vetv_a">Mature viewers</string>
+    <string name="description_vetv_tu" translatable="false">For all ages</string>
+    <string name="description_vetv_su" translatable="false">Parental guidance for young viewers</string>
+    <string name="description_vetv_a" translatable="false">Mature viewers</string>
 
     <!-- TV content rating system strings for ZA TV -->
     <string name="display_name_zatv" translatable="false">ZA-TV</string>
@@ -5504,19 +5508,22 @@
     <string name="display_name_zatv_16" translatable="false">16</string>
     <string name="display_name_zatv_18" translatable="false">18</string>
     <string name="display_name_zatv_r18" translatable="false">R18</string>
-    <string name="description_zatv_d">Drug\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
-    <string name="description_zatv_v">Violence\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
-    <string name="description_zatv_n">Nudity\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
-    <string name="description_zatv_p">Prejudice\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
-    <string name="description_zatv_s">Sex\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
-    <string name="description_zatv_l">Language\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
-    <string name="description_zatv_f">This is a program/film that does not contain any obscenity, and is suitable for family viewing. A logo must be displayed in the corner of the screen for 30 seconds after each commercial break</string>
-    <string name="description_zatv_pg">Children under 6 may watch this program/film, but must be accompanied by an adult. This program contains an adult related theme, which might include very mild language, violence and sexual innuendo. A logo must be displayed in the corner of the screen for one minute after each commercial break</string>
-    <string name="description_zatv_13">Children under 13 are prohibited from watching this program/film. This program contains mild language, violence and sexual innuendo. A logo must be displayed in the corner of the screen for two minutes after each commercial break</string>
-    <string name="description_zatv_16">Children under 16 are prohibited from watching this program/film. It contains moderate violence, language, and some sexual situations. In the case of television, this program may only be broadcast after 9pm-4:30am. A logo must be displayed in the corner of the screen for five minutes after each commercial break. A full-screen warning must be issued before the start of the program. If the program is longer than an hour, a warning must be displayed every half an hour</string>
-    <string name="description_zatv_18">Children under 18 are prohibited from watching this program/film. It contains extreme violence, language and/or graphic sexual content. In the case of television, this program may only be broadcast from 10pm-4:30am. A logo must be displayed in the corner of the screen for the duration of the program. A full-screen warning must be issued before the start of the program and after each commercial break</string>
-    <string name="description_zatv_r18">This is reserved for films of an extreme sexual nature (pornography). R18 films may only be distributed in the form of video and DVD in a controlled environment (e.g. Adult Shops). No public viewing of this film may take place. R18 films may not be broadcast on television and in cinemas</string>
+    <string name="description_zatv_d" translatable="false">Drug\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
+    <string name="description_zatv_v" translatable="false">Violence\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
+    <string name="description_zatv_n" translatable="false">Nudity\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
+    <string name="description_zatv_p" translatable="false">Prejudice\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
+    <string name="description_zatv_s" translatable="false">Sex\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
+    <string name="description_zatv_l" translatable="false">Language\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
+    <string name="description_zatv_f" translatable="false">This is a program/film that does not contain any obscenity, and is suitable for family viewing. A logo must be displayed in the corner of the screen for 30 seconds after each commercial break</string>
+    <string name="description_zatv_pg" translatable="false">Children under 6 may watch this program/film, but must be accompanied by an adult. This program contains an adult related theme, which might include very mild language, violence and sexual innuendo. A logo must be displayed in the corner of the screen for one minute after each commercial break</string>
+    <string name="description_zatv_13" translatable="false">Children under 13 are prohibited from watching this program/film. This program contains mild language, violence and sexual innuendo. A logo must be displayed in the corner of the screen for two minutes after each commercial break</string>
+    <string name="description_zatv_16" translatable="false">Children under 16 are prohibited from watching this program/film. It contains moderate violence, language, and some sexual situations. In the case of television, this program may only be broadcast after 9pm-4:30am. A logo must be displayed in the corner of the screen for five minutes after each commercial break. A full-screen warning must be issued before the start of the program. If the program is longer than an hour, a warning must be displayed every half an hour</string>
+    <string name="description_zatv_18" translatable="false">Children under 18 are prohibited from watching this program/film. It contains extreme violence, language and/or graphic sexual content. In the case of television, this program may only be broadcast from 10pm-4:30am. A logo must be displayed in the corner of the screen for the duration of the program. A full-screen warning must be issued before the start of the program and after each commercial break</string>
+    <string name="description_zatv_r18" translatable="false">This is reserved for films of an extreme sexual nature (pornography). R18 films may only be distributed in the form of video and DVD in a controlled environment (e.g. Adult Shops). No public viewing of this film may take place. R18 films may not be broadcast on television and in cinemas</string>
 
     <!-- [CHAR_LIMIT=NONE] Battery saver: Feature description -->
     <string name="battery_saver_description">To help improve battery life, battery saver reduces your device’s performance and limits vibration and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery saver turns off automatically when your device is charging</string>
+
+    <!-- [CHAR_LIMIT=NONE] Zen mode: Condition summary for built-in downtime condition, if active -->
+    <string name="downtime_condition_summary">Until your downtime ends at <xliff:g id="formattedTime" example="10.00 PM">%1$s</xliff:g></string>
 </resources>
diff --git a/core/res/res/values/styles_leanback.xml b/core/res/res/values/styles_leanback.xml
index 256ef00..72735f7 100644
--- a/core/res/res/values/styles_leanback.xml
+++ b/core/res/res/values/styles_leanback.xml
@@ -39,25 +39,29 @@
     <style name="TextAppearance.Leanback.FormWizard" parent="@style/TextAppearance.Material">
         <item name="textSize">18sp</item>
         <item name="fontFamily">sans-serif-light</item>
+        <item name="textColor">?attr/textColorPrimary</item>
     </style>
 
     <style name="TextAppearance.Leanback.FormWizard.Small" parent="@style/TextAppearance.Material.Small">
         <item name="textSize">18sp</item>
         <item name="fontFamily">sans-serif-light</item>
+        <item name="textColor">?attr/textColorPrimary</item>
     </style>
 
     <style name="TextAppearance.Leanback.FormWizard.Medium" parent="@style/TextAppearance.Material.Medium">
         <item name="textSize">36sp</item>
         <item name="fontFamily">sans-serif-thin</item>
+        <item name="textColor">?attr/textColorPrimary</item>
     </style>
 
     <style name="TextAppearance.Leanback.FormWizard.Large" parent="@style/TextAppearance.Material.Large">
         <item name="textSize">56sp</item>
         <item name="fontFamily">sans-serif-thin</item>
+        <item name="textColor">?attr/textColorPrimary</item>
     </style>
 
     <style name="TextAppearance.Leanback.FormWizard.ListItem" parent="@style/TextAppearance.Material.Subhead">
-        <item name="textSize">18sp</item>
+        <item name="textSize">16sp</item>
         <item name="fontFamily">sans-serif-condensed</item>
     </style>
 
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 97d4bf6..f028222 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -426,7 +426,6 @@
     <style name="Widget.Material.Button" parent="Widget.Button">
         <item name="background">@drawable/btn_default_material</item>
         <item name="textAppearance">?attr/textAppearanceButton</item>
-        <item name="textColor">?attr/textColorPrimary</item>
         <item name="minHeight">48dip</item>
         <item name="minWidth">88dip</item>
         <item name="stateListAnimator">@anim/button_state_list_anim_material</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 220f5ab..2cd0948 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -610,9 +610,11 @@
   <java-symbol type="string" name="last_month" />
   <java-symbol type="string" name="launchBrowserDefault" />
   <java-symbol type="string" name="lock_to_app_toast" />
+  <java-symbol type="string" name="lock_to_app_toast_accessible" />
   <java-symbol type="string" name="lock_to_app_toast_locked" />
   <java-symbol type="string" name="lock_to_app_title" />
   <java-symbol type="string" name="lock_to_app_description" />
+  <java-symbol type="string" name="lock_to_app_description_accessible" />
   <java-symbol type="string" name="lock_to_app_negative" />
   <java-symbol type="string" name="lock_to_app_positive" />
   <java-symbol type="layout" name="lock_to_app_checkbox" />
@@ -1901,6 +1903,7 @@
   <java-symbol type="string" name="timepicker_transition_mid_radius_multiplier" />
   <java-symbol type="string" name="timepicker_transition_end_radius_multiplier" />
   <java-symbol type="string" name="battery_saver_description" />
+  <java-symbol type="string" name="downtime_condition_summary" />
 
   <java-symbol type="string" name="item_is_selected" />
   <java-symbol type="string" name="day_of_week_label_typeface" />
@@ -1979,4 +1982,7 @@
   <java-symbol type="string" name="time_picker_am_label" />
   <java-symbol type="string" name="time_picker_pm_label" />
   <java-symbol type="dimen" name="text_size_small_material" />
+  <java-symbol type="attr" name="checkMarkGravity" />
+  <java-symbol type="layout" name="select_dialog_singlechoice_material" />
+  <java-symbol type="layout" name="select_dialog_multichoice_material" />
 </resources>
diff --git a/core/res/res/values/themes_leanback.xml b/core/res/res/values/themes_leanback.xml
index 321b827..1cda843 100644
--- a/core/res/res/values/themes_leanback.xml
+++ b/core/res/res/values/themes_leanback.xml
@@ -57,5 +57,6 @@
         <item name="textAppearanceLarge">@style/TextAppearance.Leanback.FormWizard.Large</item>
         <item name="textAppearanceListItem">@style/TextAppearance.Leanback.FormWizard.ListItem</item>
         <item name="textAppearance">@style/TextAppearance.Leanback.FormWizard</item>
+        <item name="textColorPrimary">@color/primary_text_leanback_formwizard_dark</item>
     </style>
 </resources>
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index e3c03a9..0bc4fdf 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -275,7 +275,7 @@
         final int res = a.getResourceId(R.styleable.AnimatedRotateDrawable_drawable, 0);
         Drawable drawable = null;
         if (res > 0) {
-            drawable = r.getDrawable(res);
+            drawable = r.getDrawable(res, theme);
         }
 
         a.recycle();
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index 2cb7b03..14aa570 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -119,11 +119,11 @@
      *
      * @param fromId Unique identifier of the starting keyframe
      * @param toId Unique identifier of the ending keyframe
-     * @param transition An animatable drawable to use as a transition, may not be null
+     * @param transition An {@link Animatable} drawable to use as a transition, may not be null
      * @param reversible Whether the transition can be reversed
      */
-    public void addTransition(int fromId, int toId, @NonNull Drawable transition,
-            boolean reversible) {
+    public <T extends Drawable & Animatable> void addTransition(int fromId, int toId,
+            @NonNull T transition, boolean reversible) {
         if (transition == null) {
             throw new IllegalArgumentException("Transition drawable must not be null");
         }
@@ -427,7 +427,7 @@
 
         final Drawable dr;
         if (drawableRes != 0) {
-            dr = r.getDrawable(drawableRes);
+            dr = r.getDrawable(drawableRes, theme);
         } else {
             int type;
             while ((type = parser.next()) == XmlPullParser.TEXT) {
@@ -473,7 +473,7 @@
 
         final Drawable dr;
         if (drawableRes != 0) {
-            dr = r.getDrawable(drawableRes);
+            dr = r.getDrawable(drawableRes, theme);
         } else {
             int type;
             while ((type = parser.next()) == XmlPullParser.TEXT) {
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index cef3377..5318fa7 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -313,7 +313,7 @@
 
             Drawable dr;
             if (drawableRes != 0) {
-                dr = r.getDrawable(drawableRes);
+                dr = r.getDrawable(drawableRes, theme);
             } else {
                 while ((type=parser.next()) == XmlPullParser.TEXT) {
                     // Empty
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index f5e63ae..40adf94 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -16,6 +16,7 @@
 
 package android.graphics.drawable;
 
+import android.annotation.NonNull;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
@@ -27,6 +28,7 @@
 import android.graphics.ColorFilter;
 import android.graphics.Insets;
 import android.graphics.Matrix;
+import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
@@ -598,6 +600,16 @@
     }
 
     @Override
+    public void getOutline(@NonNull Outline outline) {
+        super.getOutline(outline);
+        if (mBitmapState.mBitmap.hasAlpha()) {
+            // Bitmaps with alpha can't report a non-0 alpha,
+            // since they may not fill their rectangular bounds
+            outline.setAlpha(0.0f);
+        }
+    }
+
+    @Override
     public void setAlpha(int alpha) {
         final int oldAlpha = mBitmapState.mPaint.getAlpha();
         if (alpha != oldAlpha) {
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 983eb3b..63b9e02 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -436,7 +436,7 @@
                 com.android.internal.R.styleable.RotateDrawable_drawable, 0);
         Drawable drawable = null;
         if (res > 0) {
-            drawable = r.getDrawable(res);
+            drawable = r.getDrawable(res, theme);
         }
 
         a.recycle();
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 766e681..9ac6927 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -49,7 +49,7 @@
 import java.util.Stack;
 
 /**
- * This lets you create a drawable based on an XML vector graphic It can be
+ * This lets you create a drawable based on an XML vector graphic. It can be
  * defined in an XML file with the <code>&lt;vector></code> element.
  * <p/>
  * The vector drawable has the following elements:
diff --git a/graphics/java/android/graphics/drawable/shapes/Shape.java b/graphics/java/android/graphics/drawable/shapes/Shape.java
index 589fbaa..eab8666 100644
--- a/graphics/java/android/graphics/drawable/shapes/Shape.java
+++ b/graphics/java/android/graphics/drawable/shapes/Shape.java
@@ -16,6 +16,7 @@
 
 package android.graphics.drawable.shapes;
 
+import android.annotation.NonNull;
 import android.graphics.Canvas;
 import android.graphics.Outline;
 import android.graphics.Paint;
@@ -93,11 +94,12 @@
     protected void onResize(float width, float height) {}
 
     /**
-     * Compute the Outline of the shape.
+     * Compute the Outline of the shape and return it in the supplied Outline
+     * parameter. The default implementation does nothing and {@code outline} is not changed.
      *
-     * The default implementation does not supply an outline.
+     * @param outline The Outline to be populated with the result. Should not be null.
      */
-    public void getOutline(Outline outline) {}
+    public void getOutline(@NonNull Outline outline) {}
 
     @Override
     public Shape clone() throws CloneNotSupportedException {
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 7661d58..f963a4e 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -802,11 +802,16 @@
 const String8 ResStringPool::string8ObjectAt(size_t idx) const
 {
     size_t len;
-    const char *str = (const char*)string8At(idx, &len);
+    const char *str = string8At(idx, &len);
     if (str != NULL) {
-        return String8(str);
+        return String8(str, len);
     }
-    return String8(stringAt(idx, &len));
+
+    const char16_t *str16 = stringAt(idx, &len);
+    if (str16 != NULL) {
+        return String8(str16, len);
+    }
+    return String8();
 }
 
 const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const
@@ -2691,6 +2696,9 @@
             case ResTable_config::DENSITY_XXHIGH:
                 res.append("xxhdpi");
                 break;
+            case ResTable_config::DENSITY_XXXHIGH:
+                res.append("xxxhdpi");
+                break;
             case ResTable_config::DENSITY_NONE:
                 res.append("nodpi");
                 break;
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 5ecd77a..78d569d 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "RT-Animator"
-
 #include "Animator.h"
 
 #include <inttypes.h>
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 15bed58..054a164 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "DamageAccumulator"
-
 #include "DamageAccumulator.h"
 
 #include <cutils/log.h>
@@ -26,12 +24,6 @@
 namespace android {
 namespace uirenderer {
 
-NullDamageAccumulator NullDamageAccumulator::sInstance;
-
-NullDamageAccumulator* NullDamageAccumulator::instance() {
-    return &sInstance;
-}
-
 enum TransformType {
     TransformInvalid = 0,
     TransformRenderNode,
@@ -60,6 +52,30 @@
     mHead->type = TransformNone;
 }
 
+static void computeTransformImpl(const DirtyStack* currentFrame, Matrix4* outMatrix) {
+    if (currentFrame->prev != currentFrame) {
+        computeTransformImpl(currentFrame->prev, outMatrix);
+    }
+    switch (currentFrame->type) {
+    case TransformRenderNode:
+        currentFrame->renderNode->applyViewPropertyTransforms(*outMatrix);
+        break;
+    case TransformMatrix4:
+        outMatrix->multiply(*currentFrame->matrix4);
+        break;
+    case TransformNone:
+        // nothing to be done
+        break;
+    default:
+        LOG_ALWAYS_FATAL("Tried to compute transform with an invalid type: %d", currentFrame->type);
+    }
+}
+
+void DamageAccumulator::computeCurrentTransform(Matrix4* outMatrix) const {
+    outMatrix->loadIdentity();
+    computeTransformImpl(mHead, outMatrix);
+}
+
 void DamageAccumulator::pushCommon() {
     if (!mHead->next) {
         DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h
index 90d9425..6f0bd8c 100644
--- a/libs/hwui/DamageAccumulator.h
+++ b/libs/hwui/DamageAccumulator.h
@@ -31,18 +31,7 @@
 class RenderNode;
 class Matrix4;
 
-class IDamageAccumulator {
-public:
-    virtual void pushTransform(const RenderNode* transform) = 0;
-    virtual void pushTransform(const Matrix4* transform) = 0;
-    virtual void popTransform() = 0;
-    virtual void dirty(float left, float top, float right, float bottom) = 0;
-    virtual void peekAtDirty(SkRect* dest) = 0;
-protected:
-    virtual ~IDamageAccumulator() {}
-};
-
-class DamageAccumulator : public IDamageAccumulator {
+class DamageAccumulator {
     PREVENT_COPY_AND_ASSIGN(DamageAccumulator);
 public:
     DamageAccumulator();
@@ -51,17 +40,19 @@
     // Push a transform node onto the stack. This should be called prior
     // to any dirty() calls. Subsequent calls to dirty()
     // will be affected by the transform when popTransform() is called.
-    virtual void pushTransform(const RenderNode* transform);
-    virtual void pushTransform(const Matrix4* transform);
+    void pushTransform(const RenderNode* transform);
+    void pushTransform(const Matrix4* transform);
 
     // Pops a transform node from the stack, propagating the dirty rect
     // up to the parent node. Returns the IDamageTransform that was just applied
-    virtual void popTransform();
+    void popTransform();
 
-    virtual void dirty(float left, float top, float right, float bottom);
+    void dirty(float left, float top, float right, float bottom);
 
     // Returns the current dirty area, *NOT* transformed by pushed transforms
-    virtual void peekAtDirty(SkRect* dest);
+    void peekAtDirty(SkRect* dest);
+
+    void computeCurrentTransform(Matrix4* outMatrix) const;
 
     void finish(SkRect* totalDirty);
 
@@ -74,24 +65,6 @@
     DirtyStack* mHead;
 };
 
-class NullDamageAccumulator : public IDamageAccumulator {
-    PREVENT_COPY_AND_ASSIGN(NullDamageAccumulator);
-public:
-    virtual void pushTransform(const RenderNode* transform) { }
-    virtual void pushTransform(const Matrix4* transform) { }
-    virtual void popTransform() { }
-    virtual void dirty(float left, float top, float right, float bottom) { }
-    virtual void peekAtDirty(SkRect* dest) { dest->setEmpty(); }
-
-    ANDROID_API static NullDamageAccumulator* instance();
-
-private:
-    NullDamageAccumulator() {}
-    ~NullDamageAccumulator() {}
-
-    static NullDamageAccumulator sInstance;
-};
-
 } /* namespace uirenderer */
 } /* namespace android */
 
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index f0a6e55..94162fc 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -66,7 +66,7 @@
             "prepareDirty called a second time during a recording!");
     mDisplayListData = new DisplayListData();
 
-    initializeSaveStack(0, 0, getWidth(), getHeight());
+    initializeSaveStack(0, 0, getWidth(), getHeight(), Vector3());
 
     mDirtyClip = opaque;
     mRestoreSaveCount = -1;
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index d05cabc..8639ae1 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -53,6 +53,7 @@
     deferredList = NULL;
     convexMask = NULL;
     caches.resourceCache.incrementRefcount(this);
+    rendererLightPosDirty = true;
 }
 
 Layer::~Layer() {
@@ -80,6 +81,17 @@
     }
 }
 
+void Layer::updateLightPosFromRenderer(const OpenGLRenderer& rootRenderer) {
+    if (renderer && rendererLightPosDirty) {
+        // re-init renderer's light position, based upon last cached location in window
+        Vector3 lightPos = rootRenderer.getLightCenter();
+        cachedInvTransformInWindow.mapPoint3d(lightPos);
+        renderer->initLight(lightPos, rootRenderer.getLightRadius(),
+                rootRenderer.getAmbientShadowAlpha(), rootRenderer.getSpotShadowAlpha());
+        rendererLightPosDirty = false;
+    }
+}
+
 bool Layer::resize(const uint32_t width, const uint32_t height) {
     uint32_t desiredWidth = computeIdealWidth(width);
     uint32_t desiredHeight = computeIdealWidth(height);
@@ -203,7 +215,8 @@
     }
 }
 
-void Layer::defer() {
+void Layer::defer(const OpenGLRenderer& rootRenderer) {
+    updateLightPosFromRenderer(rootRenderer);
     const float width = layer.getWidth();
     const float height = layer.getHeight();
 
@@ -253,7 +266,8 @@
     }
 }
 
-void Layer::render() {
+void Layer::render(const OpenGLRenderer& rootRenderer) {
+    updateLightPosFromRenderer(rootRenderer);
     renderer->setViewport(layer.getWidth(), layer.getHeight());
     renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
             !isBlend());
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 0bf05d0..38c29c7 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -86,6 +86,11 @@
         regionRect.translate(layer.left, layer.top);
     }
 
+    void setWindowTransform(Matrix4& windowTransform) {
+        cachedInvTransformInWindow.loadInverse(windowTransform);
+        rendererLightPosDirty = true;
+    }
+
     void updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom);
 
     inline uint32_t getWidth() const {
@@ -257,10 +262,10 @@
         return transform;
     }
 
-    void defer();
+    void defer(const OpenGLRenderer& rootRenderer);
     void cancelDefer();
     void flush();
-    void render();
+    void render(const OpenGLRenderer& rootRenderer);
 
     /**
      * Bounds of the layer.
@@ -304,6 +309,7 @@
 
 private:
     void requireRenderer();
+    void updateLightPosFromRenderer(const OpenGLRenderer& rootRenderer);
 
     Caches& caches;
 
@@ -383,6 +389,12 @@
     mat4 transform;
 
     /**
+     * Cached transform of layer in window, updated only on creation / resize
+     */
+    mat4 cachedInvTransformInWindow;
+    bool rendererLightPosDirty;
+
+    /**
      * Used to defer display lists when the layer is updated with a
      * display list.
      */
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 3fcfbc1..721ab3d 100755
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -188,8 +188,7 @@
 void OpenGLRenderer::setupFrameState(float left, float top,
         float right, float bottom, bool opaque) {
     mCaches.clearGarbage();
-
-    initializeSaveStack(left, top, right, bottom);
+    initializeSaveStack(left, top, right, bottom, mLightCenter);
     mOpaque = opaque;
     mTilingClip.set(left, top, right, bottom);
 }
@@ -481,9 +480,9 @@
         }
 
         if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) {
-            layer->render();
+            layer->render(*this);
         } else {
-            layer->defer();
+            layer->defer(*this);
         }
 
         if (inFrame) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index fc95c18..e9ca5d9 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -345,8 +345,10 @@
     }
 #endif
 
-    const Vector3& getLightCenter() const { return mLightCenter; }
+    const Vector3& getLightCenter() const { return currentSnapshot()->getRelativeLightCenter(); }
     float getLightRadius() const { return mLightRadius; }
+    uint8_t getAmbientShadowAlpha() const { return mAmbientShadowAlpha; }
+    uint8_t getSpotShadowAlpha() const { return mSpotShadowAlpha; }
 
 protected:
     /**
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index e3732a1..0db6198 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -116,6 +116,7 @@
 
 void RenderNode::prepareTree(TreeInfo& info) {
     ATRACE_CALL();
+    LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing");
 
     prepareTreeImpl(info);
 }
@@ -163,16 +164,26 @@
         return;
     }
 
+    bool transformUpdateNeeded = false;
     if (!mLayer) {
         mLayer = LayerRenderer::createRenderLayer(info.renderState, getWidth(), getHeight());
         applyLayerPropertiesToLayer(info);
         damageSelf(info);
+        transformUpdateNeeded = true;
     } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
         if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
             LayerRenderer::destroyLayer(mLayer);
             mLayer = 0;
         }
         damageSelf(info);
+        transformUpdateNeeded = true;
+    }
+
+    if (transformUpdateNeeded) {
+        // update the transform in window of the layer to reset its origin wrt light source position
+        Matrix4 windowTransform;
+        info.damageAccumulator->computeCurrentTransform(&windowTransform);
+        mLayer->setWindowTransform(windowTransform);
     }
 
     SkRect dirty;
@@ -406,7 +417,7 @@
  * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
  * matrix computation instead of the Skia 3x3 matrix + camera hackery.
  */
-void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) {
+void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) const {
     if (properties().getLeft() != 0 || properties().getTop() != 0) {
         matrix.translate(properties().getLeft(), properties().getTop());
     }
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index fa310e0..afa17d5 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -174,6 +174,8 @@
     // UI thread only!
     ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator);
 
+    void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false) const;
+
 private:
     typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair;
 
@@ -189,8 +191,6 @@
         kPositiveZChildren
     };
 
-    void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false);
-
     void computeOrderingImpl(DrawRenderNodeOp* opState,
             const SkPath* outlineOfProjectionSurface,
             Vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface,
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 6f19275..ecc47d2 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -55,7 +55,8 @@
         , empty(false)
         , alpha(s->alpha)
         , roundRectClipState(s->roundRectClipState)
-        , mViewportData(s->mViewportData) {
+        , mViewportData(s->mViewportData)
+        , mRelativeLightCenter(s->mRelativeLightCenter) {
     if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
         mTransformRoot.load(*s->transform);
         transform = &mTransformRoot;
@@ -200,6 +201,13 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void Snapshot::resetTransform(float x, float y, float z) {
+    // before resetting, map current light pos with inverse of current transform
+    Vector3 center = mRelativeLightCenter;
+    mat4 inverse;
+    inverse.loadInverse(*transform);
+    inverse.mapPoint3d(center);
+    mRelativeLightCenter = center;
+
     transform = &mTransformRoot;
     transform->loadTranslate(x, y, z);
 }
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 98e2440..ad4ee9d 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -161,6 +161,9 @@
     int getViewportHeight() const { return mViewportData.mHeight; }
     const Matrix4& getOrthoMatrix() const { return mViewportData.mOrthoMatrix; }
 
+    const Vector3& getRelativeLightCenter() const { return mRelativeLightCenter; }
+    void setRelativeLightCenter(const Vector3& lightCenter) { mRelativeLightCenter = lightCenter; }
+
     /**
      * Sets (and replaces) the current clipping outline
      */
@@ -302,6 +305,7 @@
 
     SkRegion mClipRegionRoot;
     ViewportData mViewportData;
+    Vector3 mRelativeLightCenter;
 
 }; // class Snapshot
 
diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp
index dc41157..06c5ab4 100644
--- a/libs/hwui/StatefulBaseRenderer.cpp
+++ b/libs/hwui/StatefulBaseRenderer.cpp
@@ -35,11 +35,12 @@
 }
 
 void StatefulBaseRenderer::initializeSaveStack(float clipLeft, float clipTop,
-        float clipRight, float clipBottom) {
+        float clipRight, float clipBottom, const Vector3& lightCenter) {
     mSnapshot = new Snapshot(mFirstSnapshot,
             SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
     mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
     mSnapshot->fbo = getTargetFbo();
+    mSnapshot->setRelativeLightCenter(lightCenter);
     mSaveCount = 1;
 }
 
diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h
index 6d83b4c..3957d36 100644
--- a/libs/hwui/StatefulBaseRenderer.h
+++ b/libs/hwui/StatefulBaseRenderer.h
@@ -52,7 +52,8 @@
      * the render target.
      */
     virtual void setViewport(int width, int height);
-    void initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom);
+    void initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom,
+            const Vector3& lightCenter);
 
     // getters
     bool hasRectToRectTransform() const {
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index c5fc21f..0a9aeb8 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -14,9 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "OpenGLRenderer"
-#define ATRACE_TAG ATRACE_TAG_VIEW
-
 #include <utils/JenkinsHash.h>
 #include <utils/Trace.h>
 
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index de09755..331f157 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -65,7 +65,7 @@
         , frameTimeMs(0)
         , animationHook(NULL)
         , prepareTextures(mode == MODE_FULL)
-        , damageAccumulator(NullDamageAccumulator::instance())
+        , damageAccumulator(NULL)
         , renderState(renderState)
         , renderer(NULL)
         , errorHandler(NULL)
@@ -88,8 +88,9 @@
     // TODO: Remove this? Currently this is used to signal to stop preparing
     // textures if we run out of cache space.
     bool prepareTextures;
-    // Must not be null
-    IDamageAccumulator* damageAccumulator;
+
+    // Must not be null during actual usage
+    DamageAccumulator* damageAccumulator;
     RenderState& renderState;
     // The renderer that will be drawing the next frame. Use this to push any
     // layer updates or similar. May be NULL.
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 756f660..e673b0d 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "CanvasContext"
-
 #include "CanvasContext.h"
 
 #include <private/hwui/DrawGlInfo.h>
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 986e808..d9b96f6c 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "RenderProxy"
-
 #include "RenderProxy.h"
 
 #include "CanvasContext.h"
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 03e98d5..403e164 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "RenderThread"
-
 #include "RenderThread.h"
 
+#if defined(HAVE_PTHREADS)
+#include <sys/resource.h>
+#endif
 #include <gui/DisplayEventReceiver.h>
 #include <utils/Log.h>
 
@@ -244,6 +245,9 @@
 }
 
 bool RenderThread::threadLoop() {
+#if defined(HAVE_PTHREADS)
+    setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
+#endif
     initThreadLocals();
 
     int timeoutMillis = -1;
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index 3d2b0d9..cb5401c 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -15,6 +15,9 @@
  */
 
 #include <sys/sysinfo.h>
+#if defined(HAVE_PTHREADS)
+#include <sys/resource.h>
+#endif
 
 #include "TaskManager.h"
 #include "Task.h"
@@ -79,6 +82,13 @@
 // Thread
 ///////////////////////////////////////////////////////////////////////////////
 
+status_t TaskManager::WorkerThread::readyToRun() {
+#if defined(HAVE_PTHREADS)
+    setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND);
+#endif
+    return NO_ERROR;
+}
+
 bool TaskManager::WorkerThread::threadLoop() {
     mSignal.wait();
     Vector<TaskWrapper> tasks;
diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h
index f2a216f..5a933ab 100644
--- a/libs/hwui/thread/TaskManager.h
+++ b/libs/hwui/thread/TaskManager.h
@@ -84,6 +84,7 @@
         void exit();
 
     private:
+        virtual status_t readyToRun();
         virtual bool threadLoop();
 
         // Lock for the list of tasks
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index bd50142..6a76a71 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1672,24 +1672,6 @@
 
     /**
      * @hide
-     * Checks whether any local or remote media playback is active.
-     * Local playback refers to playback for instance on the device's speakers or wired headphones.
-     * Remote playback refers to playback for instance on a wireless display mirroring the
-     *    devices's, or on a device using a Cast-like protocol.
-     * @return true if media playback, from which the device is aware, is active.
-     */
-    public boolean isLocalOrRemoteMusicActive() {
-        IAudioService service = getService();
-        try {
-            return service.isLocalOrRemoteMusicActive();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in isLocalOrRemoteMusicActive()", e);
-            return false;
-        }
-    }
-
-    /**
-     * @hide
      * Checks whether the current audio focus is exclusive.
      * @return true if the top of the audio focus stack requested focus
      *     with {@link #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}
diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java
new file mode 100644
index 0000000..6f1bdef
--- /dev/null
+++ b/media/java/android/media/AudioManagerInternal.java
@@ -0,0 +1,34 @@
+/*
+ * 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.media;
+
+import com.android.server.LocalServices;
+
+/**
+ * Class for system services to access extra AudioManager functionality. The
+ * AudioService is responsible for registering an implementation with
+ * {@link LocalServices}.
+ *
+ * @hide
+ */
+public abstract class AudioManagerInternal {
+
+    public abstract void adjustStreamVolumeForUid(int streamType, int direction, int flags,
+            String callingPackage, int uid);
+
+    public abstract void setStreamVolumeForUid(int streamType, int direction, int flags,
+            String callingPackage, int uid);
+}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 6d0bfee..0af2457 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -79,6 +79,7 @@
 
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.util.XmlUtils;
+import com.android.server.LocalServices;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -481,11 +482,6 @@
     final RemoteCallbackList<IAudioRoutesObserver> mRoutesObservers
             = new RemoteCallbackList<IAudioRoutesObserver>();
 
-    /**
-     * A fake stream type to match the notion of remote media playback
-     */
-    public final static int STREAM_REMOTE_MUSIC = -200;
-
     // Devices for which the volume is fixed and VolumePanel slider should be disabled
     int mFixedVolumeDevices = AudioSystem.DEVICE_OUT_HDMI |
             AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
@@ -635,6 +631,7 @@
         mMasterVolumeRamp = context.getResources().getIntArray(
                 com.android.internal.R.array.config_masterVolumeRamp);
 
+        LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal());
     }
 
     public void systemReady() {
@@ -896,27 +893,6 @@
     ///////////////////////////////////////////////////////////////////////////
     // IPC methods
     ///////////////////////////////////////////////////////////////////////////
-    /** @see AudioManager#isLocalOrRemoteMusicActive() */
-    public boolean isLocalOrRemoteMusicActive() {
-        if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
-            // local / wired / BT playback active
-            if (DEBUG_VOL) Log.d(TAG, "isLocalOrRemoteMusicActive(): local");
-            return true;
-        }
-        if (mMediaFocusControl.checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
-            // remote "cast-like" playback active
-            if (DEBUG_VOL) Log.d(TAG, "isLocalOrRemoteMusicActive(): has PLAYBACK_TYPE_REMOTE");
-            return true;
-        }
-        if (AudioSystem.isStreamActiveRemotely(AudioSystem.STREAM_MUSIC, 0)) {
-            // remote submix playback active
-            if (DEBUG_VOL) Log.d(TAG, "isLocalOrRemoteMusicActive(): remote submix");
-            return true;
-        }
-        if (DEBUG_VOL) Log.d(TAG, "isLocalOrRemoteMusicActive(): no");
-        return false;
-    }
-
     /** @see AudioManager#adjustVolume(int, int) */
     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
             String callingPackage) {
@@ -930,9 +906,8 @@
         }
         final int resolvedStream = mStreamVolumeAlias[streamType];
 
-        // Play sounds on STREAM_RING and STREAM_REMOTE_MUSIC only.
-        if ((streamType != STREAM_REMOTE_MUSIC) &&
-                (flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
+        // Play sounds on STREAM_RING only.
+        if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
                 resolvedStream != AudioSystem.STREAM_RING) {
             flags &= ~AudioManager.FLAG_PLAY_SOUND;
         }
@@ -945,17 +920,17 @@
             if (DEBUG_VOL) Log.d(TAG, "Volume controller suppressed adjustment");
         }
 
-        if (streamType == STREAM_REMOTE_MUSIC) {
-            // TODO bounce it to MediaSessionService to find an appropriate
-            // session
-        } else {
-            adjustStreamVolume(streamType, direction, flags, callingPackage);
-        }
+        adjustStreamVolume(streamType, direction, flags, callingPackage);
     }
 
     /** @see AudioManager#adjustStreamVolume(int, int, int) */
     public void adjustStreamVolume(int streamType, int direction, int flags,
             String callingPackage) {
+        adjustStreamVolume(streamType, direction, flags, callingPackage, Binder.getCallingUid());
+    }
+
+    private void adjustStreamVolume(int streamType, int direction, int flags,
+            String callingPackage, int uid) {
         if (mUseFixedVolume) {
             return;
         }
@@ -984,8 +959,8 @@
             return;
         }
 
-        if (mAppOps.noteOp(STEAM_VOLUME_OPS[streamTypeAlias], Binder.getCallingUid(),
-                callingPackage) != AppOpsManager.MODE_ALLOWED) {
+        if (mAppOps.noteOp(STEAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
+                != AppOpsManager.MODE_ALLOWED) {
             return;
         }
 
@@ -1161,6 +1136,11 @@
 
     /** @see AudioManager#setStreamVolume(int, int, int) */
     public void setStreamVolume(int streamType, int index, int flags, String callingPackage) {
+        setStreamVolume(streamType, index, flags, callingPackage, Binder.getCallingUid());
+    }
+
+    private void setStreamVolume(int streamType, int index, int flags, String callingPackage,
+            int uid) {
         if (mUseFixedVolume) {
             return;
         }
@@ -1179,8 +1159,8 @@
             return;
         }
 
-        if (mAppOps.noteOp(STEAM_VOLUME_OPS[streamTypeAlias], Binder.getCallingUid(),
-                callingPackage) != AppOpsManager.MODE_ALLOWED) {
+        if (mAppOps.noteOp(STEAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
+                != AppOpsManager.MODE_ALLOWED) {
             return;
         }
 
@@ -1885,10 +1865,6 @@
                 }
             }
             int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
-            if (streamType == STREAM_REMOTE_MUSIC) {
-                // here handle remote media playback the same way as local playback
-                streamType = AudioManager.STREAM_MUSIC;
-            }
             int device = getDeviceForStream(streamType);
             int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device);
             setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true);
@@ -2973,12 +2949,6 @@
                     if (DEBUG_VOL)
                         Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
                     return AudioSystem.STREAM_MUSIC;
-                } else
-                    if (mMediaFocusControl.checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC))
-                    {
-                        if (DEBUG_VOL)
-                            Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
-                        return STREAM_REMOTE_MUSIC;
                     } else {
                         if (DEBUG_VOL)
                             Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING b/c default");
@@ -2992,19 +2962,8 @@
             break;
         case PLATFORM_TELEVISION:
             if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
-                if (isAfMusicActiveRecently(DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS)) {
-                    if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: forcing STREAM_MUSIC");
+                    // TV always defaults to STREAM_MUSIC
                     return AudioSystem.STREAM_MUSIC;
-                } else if (mMediaFocusControl.checkUpdateRemoteStateIfActive(
-                                                                        AudioSystem.STREAM_MUSIC)) {
-                    if (DEBUG_VOL)
-                        Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
-                    return STREAM_REMOTE_MUSIC;
-                } else {
-                    if (DEBUG_VOL) Log.v(TAG,
-                            "getActiveStreamType: using STREAM_MUSIC as default");
-                    return AudioSystem.STREAM_MUSIC;
-                }
             }
             break;
         default:
@@ -3027,12 +2986,6 @@
                 if (isAfMusicActiveRecently(DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS)) {
                     if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: forcing STREAM_MUSIC");
                     return AudioSystem.STREAM_MUSIC;
-                } else
-                    if (mMediaFocusControl.checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC))
-                    {
-                        if (DEBUG_VOL)
-                            Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
-                        return STREAM_REMOTE_MUSIC;
                 } else {
                     if (DEBUG_VOL) Log.v(TAG,
                             "getActiveStreamType: using STREAM_NOTIFICATION as default");
@@ -5347,6 +5300,24 @@
         }
     }
 
+    /**
+     * Interface for system components to get some extra functionality through
+     * LocalServices.
+     */
+    final class AudioServiceInternal extends AudioManagerInternal {
+        @Override
+        public void adjustStreamVolumeForUid(int streamType, int direction, int flags,
+                String callingPackage, int uid) {
+            adjustStreamVolume(streamType, direction, flags, callingPackage, uid);
+        }
+
+        @Override
+        public void setStreamVolumeForUid(int streamType, int direction, int flags,
+                String callingPackage, int uid) {
+            setStreamVolume(streamType, direction, flags, callingPackage, uid);
+        }
+    }
+
     //==========================================================================================
     // Audio policy management
     //==========================================================================================
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 7318660..6477055 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -37,8 +37,6 @@
  */
 interface IAudioService {
 
-    boolean isLocalOrRemoteMusicActive();
-
     void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
             String callingPackage);
 
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index 0aaaf46..adc8391 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -793,7 +793,7 @@
         final ComponentName listenerComponent = new ComponentName(mContext,
                 mOnClientUpdateListener.getClass());
         mSessionManager.addActiveSessionsListener(mSessionListener, listenerComponent,
-                UserHandle.myUserId());
+                UserHandle.myUserId(), null);
         mSessionListener.onActiveSessionsChanged(mSessionManager
                 .getActiveSessions(listenerComponent));
         if (DEBUG) {
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 3518458..5764bd1 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -48,8 +48,8 @@
     PendingIntent getLaunchPendingIntent();
     long getFlags();
     ParcelableVolumeInfo getVolumeAttributes();
-    void adjustVolume(int direction, int flags);
-    void setVolumeTo(int value, int flags);
+    void adjustVolume(int direction, int flags, String packageName);
+    void setVolumeTo(int value, int flags, String packageName);
 
     IMediaRouterDelegate createMediaRouterDelegate(IMediaRouterStateCallback callback);
 
diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl
index 78cd699..cf31767 100644
--- a/media/java/android/media/session/ISessionControllerCallback.aidl
+++ b/media/java/android/media/session/ISessionControllerCallback.aidl
@@ -27,6 +27,7 @@
  */
 oneway interface ISessionControllerCallback {
     void onEvent(String event, in Bundle extras);
+    void onSessionDestroyed();
 
     // These callbacks are for the TransportController
     void onPlaybackStateChanged(in PlaybackState state);
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index f6e189a..63c85f8 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -63,6 +63,7 @@
     private static final int MSG_UPDATE_QUEUE = 5;
     private static final int MSG_UPDATE_QUEUE_TITLE = 6;
     private static final int MSG_UPDATE_EXTRAS = 7;
+    private static final int MSG_DESTROYED = 8;
 
     private final ISessionController mSessionBinder;
 
@@ -310,7 +311,7 @@
      */
     public void setVolumeTo(int value, int flags) {
         try {
-            mSessionBinder.setVolumeTo(value, flags);
+            mSessionBinder.setVolumeTo(value, flags, mContext.getPackageName());
         } catch (RemoteException e) {
             Log.wtf(TAG, "Error calling setVolumeTo.", e);
         }
@@ -331,7 +332,7 @@
      */
     public void adjustVolume(int direction, int flags) {
         try {
-            mSessionBinder.adjustVolume(direction, flags);
+            mSessionBinder.adjustVolume(direction, flags, mContext.getPackageName());
         } catch (RemoteException e) {
             Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
         }
@@ -508,6 +509,13 @@
      */
     public static abstract class Callback {
         /**
+         * Override to handle the session being destroyed. The session is no
+         * longer valid after this call and calls to it will be ignored.
+         */
+        public void onSessionDestroyed() {
+        }
+
+        /**
          * Override to handle custom events sent by the session owner without a
          * specified interface. Controllers should only handle these for
          * sessions they own.
@@ -863,6 +871,14 @@
         }
 
         @Override
+        public void onSessionDestroyed() {
+            MediaController controller = mController.get();
+            if (controller != null) {
+                controller.postMessage(MSG_DESTROYED, null, null);
+            }
+        }
+
+        @Override
         public void onEvent(String event, Bundle extras) {
             MediaController controller = mController.get();
             if (controller != null) {
@@ -955,6 +971,9 @@
                 case MSG_UPDATE_VOLUME:
                     mCallback.onVolumeInfoChanged((VolumeInfo) msg.obj);
                     break;
+                case MSG_DESTROYED:
+                    mCallback.onSessionDestroyed();
+                    break;
             }
         }
 
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index a182982..aa196a9 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -290,7 +290,6 @@
             if (DEBUG) {
                 Log.d(TAG, "addMediaButtonListener already added " + pi);
             }
-            return;
         }
         holder.mMediaButtonListener = new MediaButtonListener(pi, context);
         // TODO determine if handling transport performer commands should also
@@ -468,7 +467,11 @@
                 mSessions.remove(mPi);
             } else if (mCb == null) {
                 mCb = new SessionCallback();
-                mSession.setCallback(mCb);
+                Handler handler = null;
+                if (Looper.myLooper() == null) {
+                    handler = new Handler(Looper.getMainLooper());
+                }
+                mSession.setCallback(mCb, handler);
             }
         }
 
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 84983b9..c2fb5a3 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -23,6 +23,7 @@
 import android.media.AudioManager;
 import android.media.IRemoteVolumeController;
 import android.media.session.ISessionManager;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -37,11 +38,11 @@
 
 /**
  * Provides support for interacting with {@link MediaSession media sessions}
- * that applications have published to express their ongoing media playback state.
+ * that applications have published to express their ongoing media playback
+ * state.
  * <p>
  * Use <code>Context.getSystemService(Context.MEDIA_SESSION_SERVICE)</code> to
  * get an instance of this class.
- * <p>
  *
  * @see MediaSession
  * @see MediaController
@@ -133,15 +134,17 @@
      * the calling app. You may also retrieve this list if your app is an
      * enabled notification listener using the
      * {@link NotificationListenerService} APIs, in which case you must pass the
-     * {@link ComponentName} of your enabled listener.
+     * {@link ComponentName} of your enabled listener. Updates will be posted to
+     * the thread that registered the listener.
      *
      * @param sessionListener The listener to add.
      * @param notificationListener The enabled notification listener component.
      *            May be null.
      */
-    public void addActiveSessionsListener(SessionListener sessionListener,
-            ComponentName notificationListener) {
-        addActiveSessionsListener(sessionListener, notificationListener, UserHandle.myUserId());
+    public void addActiveSessionsListener(@NonNull SessionListener sessionListener,
+            @Nullable ComponentName notificationListener) {
+        addActiveSessionsListener(sessionListener, notificationListener, UserHandle.myUserId(),
+                null);
     }
 
     /**
@@ -157,13 +160,18 @@
      * @param notificationListener The enabled notification listener component.
      *            May be null.
      * @param userId The userId to listen for changes on.
+     * @param handler The handler to post updates on.
      * @hide
      */
     public void addActiveSessionsListener(@NonNull SessionListener sessionListener,
-            @Nullable ComponentName notificationListener, int userId) {
+            @Nullable ComponentName notificationListener, int userId, @Nullable Handler handler) {
         if (sessionListener == null) {
             throw new IllegalArgumentException("listener may not be null");
         }
+        if (handler == null) {
+            handler = new Handler();
+        }
+        sessionListener.setHandler(handler);
         try {
             mService.addSessionsListener(sessionListener.mStub, notificationListener, userId);
         } catch (RemoteException e) {
@@ -253,6 +261,7 @@
      */
     public static abstract class SessionListener {
         private final Context mContext;
+        private Handler mHandler;
 
         public SessionListener(Context context) {
             mContext = context;
@@ -269,15 +278,27 @@
         public abstract void onActiveSessionsChanged(
                 @Nullable List<MediaController> controllers);
 
+        private final void setHandler(Handler handler) {
+            mHandler = handler;
+        }
+
         private final IActiveSessionsListener.Stub mStub = new IActiveSessionsListener.Stub() {
             @Override
-            public void onActiveSessionsChanged(List<MediaSession.Token> tokens) {
-                ArrayList<MediaController> controllers = new ArrayList<MediaController>();
-                int size = tokens.size();
-                for (int i = 0; i < size; i++) {
-                    controllers.add(new MediaController(mContext, tokens.get(i)));
+            public void onActiveSessionsChanged(final List<MediaSession.Token> tokens) {
+                if (mHandler != null) {
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            ArrayList<MediaController> controllers
+                                    = new ArrayList<MediaController>();
+                            int size = tokens.size();
+                            for (int i = 0; i < size; i++) {
+                                controllers.add(new MediaController(mContext, tokens.get(i)));
+                            }
+                            SessionListener.this.onActiveSessionsChanged(controllers);
+                        }
+                    });
                 }
-                SessionListener.this.onActiveSessionsChanged(controllers);
             }
         };
     }
diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java
index 015daee..405ef22 100644
--- a/media/java/android/media/tv/TvContentRating.java
+++ b/media/java/android/media/tv/TvContentRating.java
@@ -34,42 +34,87 @@
  * strings, or a TV input service defined strings.
  * TV input service defined strings are in an xml file defined in <code>&lt;{@link
  * android.R.styleable#TvInputService tv-input}&gt;</code> with the {@link
- * android.R.attr#contentRatingSystemXml contentRatingSystemXml} attribute by the TV input service.
+ * android.R.attr#tvContentRatingDescription tvContentRatingDescription} attribute by the TV input
+ * service.
  *
- * <h3> Content Rating System XML format </h3>
- * The XML file for publishing content rating system should follow the DTD bellow:
+ * <h3> Example: Rating system definition for the TV Parental Guidelines</h3>
+ * The following XML example shows how the TV Parental Guidelines in United States can be defined:
  * <p><pre class="prettyprint">
- * &lt;?xml version="1.0" encoding="UTF-8"?&gt;
- * &lt;!DOCTYPE rating-systems [
- *     &lt;!ELEMENT rating-system-definitions (rating-system-definition+)&gt;
- *     &lt;!ATTLIST rating-system-definitions
- *         version     CDATA #REQUIRED&gt;
- *     &lt;!ELEMENT rating-system-definition (
- *         (sub-rating-definition*, rating-definition, sub-rating-definition*)+, order*)&gt;
- *     &lt;!ATTLIST rating-system-definition
- *         id          ID    #REQUIRED
- *         displayName CDATA #IMPLIED
- *         description CDATA #IMPLIED
- *         country     CDATA #IMPLIED&gt;
- *     &lt;!ELEMENT sub-rating-definition EMPTY&gt;
- *     &lt;!ATTLIST sub-rating-definition
- *         id          ID    #REQUIRED
- *         displayName CDATA #IMPLIED
- *         icon        CDATA #IMPLIED
- *         description CDATA #IMPLIED&gt;
- *     &lt;!ELEMENT rating-definition (sub-rating*))&gt;
- *     &lt;!ATTLIST rating-definition
- *         id          ID    #REQUIRED
- *         displayName CDATA #IMPLIED
- *         icon        CDATA #IMPLIED
- *         description CDATA #IMPLIED&gt;
- *     &lt;!ELEMENT sub-rating EMPTY&gt;
- *     &lt;!ATTLIST sub-rating id IDREF #REQUIRED&gt;
- *     &lt;!ELEMENT order (rating, rating+)&gt;
- *     &lt;!ELEMENT rating EMPTY&gt;
- *     &lt;!ATTLIST rating id IDREF #REQUIRED&gt;
- * ]&gt;
- * </pre></p>
+ * {@literal
+ * <?xml version="1.0" encoding="UTF-8"?>
+ * <rating-system-definitions version="1.0">
+ *     <rating-system-definition id="US_TV"
+ *         displayName="US-TV"
+ *         description="The TV Parental Guidelines"
+ *         country="US">
+ *         <sub-rating-definition id="US_TV_D"
+ *             displayName="D"
+ *             description="Suggestive dialogue (Usually means talks about sex)" />
+ *         <sub-rating-definition id="US_TV_L"
+ *             displayName="L"
+ *             description="Coarse language" />
+ *         <sub-rating-definition id="US_TV_S"
+ *             displayName="S"
+ *             description="Sexual content" />
+ *         <sub-rating-definition id="US_TV_V"
+ *             displayName="V"
+ *             description="Violence" />
+ *         <sub-rating-definition id="US_TV_FV"
+ *             displayName="FV"
+ *             description="Fantasy violence (Children\'s programming only)" />
+ *
+ *         <rating-definition id="US_TV_Y"
+ *             displayName="TV-Y"
+ *             description="This program is designed to be appropriate for all children"
+ *             ageHint="0" />
+ *         <rating-definition id="US_TV_Y7"
+ *             displayName="TV-Y7"
+ *             description="This program is designed for children age 7 and above"
+ *             ageHint="7">
+ *             <sub-rating id="US_TV_FV" />
+ *         </rating-definition>
+ *         <rating-definition id="US_TV_G"
+ *             displayName="TV-G"
+ *             description="Most parents would find this program suitable for all ages"
+ *             ageHint="0" />
+ *         <rating-definition id="US_TV_PG"
+ *             displayName="TV-PG"
+ *             description="This program contains material that parents may find unsuitable for younger children"
+ *             ageHint="14">
+ *             <sub-rating id="US_TV_D" />
+ *             <sub-rating id="US_TV_L" />
+ *             <sub-rating id="US_TV_S" />
+ *             <sub-rating id="US_TV_V" />
+ *         </rating-definition>
+ *         <rating-definition id="US_TV_14"
+ *             displayName="TV-14"
+ *             description="This program contains some material that many parents would find unsuitable for children under 14 years of age"
+ *             ageHint="14">
+ *             <sub-rating id="US_TV_D" />
+ *             <sub-rating id="US_TV_L" />
+ *             <sub-rating id="US_TV_S" />
+ *             <sub-rating id="US_TV_V" />
+ *         </rating-definition>
+ *         <rating-definition id="US_TV_MA"
+ *             displayName="TV-MA"
+ *             description="This program is specifically designed to be viewed by adults and therefore may be unsuitable for children under 17"
+ *             ageHint="17">
+ *             <sub-rating id="US_TV_L" />
+ *             <sub-rating id="US_TV_S" />
+ *             <sub-rating id="US_TV_V" />
+ *         </rating-definition>
+ *         <order>
+ *             <rating id="US_TV_Y" />
+ *             <rating id="US_TV_Y7" />
+ *         </order>
+ *         <order>
+ *             <rating id="US_TV_G" />
+ *             <rating id="US_TV_PG" />
+ *             <rating id="US_TV_14" />
+ *             <rating id="US_TV_MA" />
+ *         </order>
+ *     </rating-system-definition>
+ * </rating-system-definitions>}</pre></p>
  *
  * <h3>System defined rating strings</h3>
  *
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index a1ca871..6ed7580 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -246,17 +246,17 @@
                 Log.d(TAG, "Settings activity loaded. [" + input.mSettingsActivity + "] for "
                         + si.name);
             }
-            int contentRatingSystemXml = sa.getResourceId(
-                    com.android.internal.R.styleable.TvInputService_contentRatingSystemXml, -1);
-            if (contentRatingSystemXml != -1) {
+            int tvContentRatingDescription = sa.getResourceId(
+                    com.android.internal.R.styleable.TvInputService_tvContentRatingDescription, -1);
+            if (tvContentRatingDescription != -1) {
                 input.mRatingSystemXmlUri = new Uri.Builder()
                         .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
                         .authority(si.packageName)
-                        .appendPath(Integer.toString(contentRatingSystemXml))
+                        .appendPath(Integer.toString(tvContentRatingDescription))
                         .build();
                 if (DEBUG) {
-                    Log.d(TAG, "Content rating xml loaded. [" + contentRatingSystemXml + "] for "
-                            + si.name);
+                    Log.d(TAG, "Content rating xml loaded. [" + tvContentRatingDescription
+                            + "] for " + si.name);
                 }
             }
             sa.recycle();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
index 3038323..b2e38fc 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
@@ -210,7 +210,7 @@
                     return pm.getDrawable(info.packageName, icon, info.applicationInfo);
                 }
             } else {
-                return context.getResources().getDrawable(icon);
+                return context.getDrawable(icon);
             }
         }
         return null;
@@ -218,19 +218,17 @@
 
     public static Drawable loadMimeIcon(
             Context context, String mimeType, String authority, String docId, int mode) {
-        final Resources res = context.getResources();
-
         if (Document.MIME_TYPE_DIR.equals(mimeType)) {
             // TODO: eventually move these hacky assets into that package
             if ("com.android.providers.media.documents".equals(authority)
                     && docId.startsWith("album")) {
-                return res.getDrawable(R.drawable.ic_doc_album);
+                return context.getDrawable(R.drawable.ic_doc_album);
             }
 
             if (mode == DocumentsActivity.State.MODE_GRID) {
-                return res.getDrawable(R.drawable.ic_grid_folder);
+                return context.getDrawable(R.drawable.ic_grid_folder);
             } else {
-                return res.getDrawable(R.drawable.ic_doc_folder);
+                return context.getDrawable(R.drawable.ic_doc_folder);
             }
         }
 
@@ -238,16 +236,14 @@
     }
 
     public static Drawable loadMimeIcon(Context context, String mimeType) {
-        final Resources res = context.getResources();
-
         if (Document.MIME_TYPE_DIR.equals(mimeType)) {
-            return res.getDrawable(R.drawable.ic_doc_folder);
+            return context.getDrawable(R.drawable.ic_doc_folder);
         }
 
         // Look for exact match first
         Integer resId = sMimeIcons.get(mimeType);
         if (resId != null) {
-            return res.getDrawable(resId);
+            return context.getDrawable(resId);
         }
 
         if (mimeType == null) {
@@ -258,15 +254,15 @@
         // Otherwise look for partial match
         final String typeOnly = mimeType.split("/")[0];
         if ("audio".equals(typeOnly)) {
-            return res.getDrawable(R.drawable.ic_doc_audio);
+            return context.getDrawable(R.drawable.ic_doc_audio);
         } else if ("image".equals(typeOnly)) {
-            return res.getDrawable(R.drawable.ic_doc_image);
+            return context.getDrawable(R.drawable.ic_doc_image);
         } else if ("text".equals(typeOnly)) {
-            return res.getDrawable(R.drawable.ic_doc_text);
+            return context.getDrawable(R.drawable.ic_doc_text);
         } else if ("video".equals(typeOnly)) {
-            return res.getDrawable(R.drawable.ic_doc_video);
+            return context.getDrawable(R.drawable.ic_doc_video);
         } else {
-            return res.getDrawable(R.drawable.ic_doc_generic);
+            return context.getDrawable(R.drawable.ic_doc_generic);
         }
     }
 
@@ -276,7 +272,7 @@
         final TypedValue outValue = new TypedValue();
         context.getTheme().resolveAttribute(tintAttrId, outValue, true);
 
-        final Drawable icon = res.getDrawable(drawableId);
+        final Drawable icon = context.getDrawable(drawableId);
         icon.mutate();
         icon.setTintList(res.getColorStateList(outValue.resourceId));
         return icon;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index 82c3048..dd75dbd 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -221,8 +221,7 @@
             final DocumentStack stack = getItem(position);
             iconMime.setImageDrawable(stack.root.loadIcon(context));
 
-            final Drawable crumb = context.getResources()
-                    .getDrawable(R.drawable.ic_breadcrumb_arrow);
+            final Drawable crumb = context.getDrawable(R.drawable.ic_breadcrumb_arrow);
             crumb.setBounds(0, 0, crumb.getIntrinsicWidth(), crumb.getIntrinsicHeight());
 
             final SpannableStringBuilder builder = new SpannableStringBuilder();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index a358798..fcfe518 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -205,7 +205,7 @@
 
     public Drawable loadIcon(Context context) {
         if (derivedIcon != 0) {
-            return context.getResources().getDrawable(derivedIcon);
+            return context.getDrawable(derivedIcon);
         } else {
             return IconUtils.loadPackageIcon(context, authority, icon);
         }
diff --git a/packages/Keyguard/res/values-h560dp/dimens.xml b/packages/Keyguard/res/values-h560dp/dimens.xml
new file mode 100644
index 0000000..1683113
--- /dev/null
+++ b/packages/Keyguard/res/values-h560dp/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<resources>
+    <dimen name="widget_big_font_size">96dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/Keyguard/res/values/dimens.xml b/packages/Keyguard/res/values/dimens.xml
index e822c82..bfc0531 100644
--- a/packages/Keyguard/res/values/dimens.xml
+++ b/packages/Keyguard/res/values/dimens.xml
@@ -161,7 +161,7 @@
     <dimen name="bottom_text_spacing_digital">-10dp</dimen>
     <dimen name="label_font_size">14dp</dimen>
     <dimen name="widget_label_font_size">16sp</dimen>
-    <dimen name="widget_big_font_size">96dp</dimen>
+    <dimen name="widget_big_font_size">88dp</dimen>
     <dimen name="big_font_size">120dp</dimen>
 
     <!-- The y translation to apply at the start in appear animations. -->
diff --git a/packages/SystemUI/res/layout/split_clock_view.xml b/packages/SystemUI/res/layout/split_clock_view.xml
index d9ba35d..87b7051 100644
--- a/packages/SystemUI/res/layout/split_clock_view.xml
+++ b/packages/SystemUI/res/layout/split_clock_view.xml
@@ -34,5 +34,15 @@
         android:layout_height="wrap_content"
         android:singleLine="true"
         android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
+        android:textSize="@dimen/qs_time_collapsed_size"
+        />
+
+    <!-- Empty text view so we have the same height when expanded/collapsed-->
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="invisible"
+        android:singleLine="true"
+        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
         />
 </com.android.systemui.statusbar.policy.SplitClockView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 4eab9c7..cc449c5 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -50,6 +50,10 @@
         android:visibility="gone"
         />
 
+    <include
+        layout="@layout/keyguard_bottom_area"
+        android:visibility="gone" />
+
     <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
         android:layout_width="match_parent"
         android:layout_height="match_parent"
@@ -115,10 +119,6 @@
 
     <include layout="@layout/status_bar_expanded_header" />
 
-    <include
-        layout="@layout/keyguard_bottom_area"
-        android:visibility="gone" />
-
     <com.android.systemui.statusbar.AlphaOptimizedView
         android:id="@+id/qs_navbar_scrim"
         android:layout_height="96dp"
diff --git a/packages/SystemUI/res/values-h560dp-xhdpi/config.xml b/packages/SystemUI/res/values-h560dp-xhdpi/config.xml
new file mode 100644
index 0000000..cf2017f
--- /dev/null
+++ b/packages/SystemUI/res/values-h560dp-xhdpi/config.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ 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
+  -->
+<resources>
+    <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
+     card. -->
+    <integer name="keyguard_max_notification_count">3</integer>
+</resources>
+
diff --git a/packages/SystemUI/res/values-h560dp-xhdpi/dimens.xml b/packages/SystemUI/res/values-h560dp-xhdpi/dimens.xml
new file mode 100644
index 0000000..f6dbc3d
--- /dev/null
+++ b/packages/SystemUI/res/values-h560dp-xhdpi/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<resources>
+    <fraction name="keyguard_clock_y_fraction_max">32.5%</fraction>
+    <fraction name="keyguard_clock_y_fraction_min">24%</fraction>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-h560dp/config.xml b/packages/SystemUI/res/values-h560dp-xxhdpi/config.xml
similarity index 100%
rename from packages/SystemUI/res/values-h560dp/config.xml
rename to packages/SystemUI/res/values-h560dp-xxhdpi/config.xml
diff --git a/packages/SystemUI/res/values-h560dp-xxhdpi/dimens.xml b/packages/SystemUI/res/values-h560dp-xxhdpi/dimens.xml
new file mode 100644
index 0000000..905e9e3
--- /dev/null
+++ b/packages/SystemUI/res/values-h560dp-xxhdpi/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<resources>
+    <fraction name="keyguard_clock_y_fraction_max">32.5%</fraction>
+    <fraction name="keyguard_clock_y_fraction_min">19.8%</fraction>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a09dfe1..5415d19 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -323,7 +323,7 @@
     <dimen name="volume_panel_z">3dp</dimen>
 
     <!-- Distance between notifications and header when they are considered to be colliding. -->
-    <dimen name="header_notifications_collide_distance">24dp</dimen>
+    <dimen name="header_notifications_collide_distance">48dp</dimen>
 
     <!-- Distance the user needs to drag vertically such that a swipe is accepted to unlock the
          device. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 50eb3e5..c24fcae 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -678,6 +678,12 @@
 
     <!-- Related to user switcher --><skip/>
 
+    <!-- Accessibility label for the button that opens the user switcher. -->
+    <string name="accessibility_multi_user_switch_switcher">Switch user</string>
+
+    <!-- Accessibility label for the button that opens the quick contact of the user. -->
+    <string name="accessibility_multi_user_switch_quick_contact">Show profile</string>
+
     <!-- Label for the adding a new user in the user switcher [CHAR LIMIT=35] -->
     <string name="user_add_user">Add user</string>
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 21b62e9..aee558f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -395,7 +395,7 @@
 
             // Scroll the view into position (just center it in the curve)
             if (scrollToNewPosition) {
-                float newScroll = mLayoutAlgorithm.getStackScrollForTaskIndex(t) - 0.5f;
+                float newScroll = mLayoutAlgorithm.getStackScrollForTask(t) - 0.5f;
                 newScroll = mStackScroller.getBoundedStackScroll(newScroll);
                 mStackScroller.animateScroll(mStackScroller.getStackScroll(), newScroll, postScrollRunnable);
             } else {
@@ -681,8 +681,28 @@
         // Notify the callback that we've removed the task and it can clean up after it
         mCb.onTaskViewDismissed(removedTask);
 
+        // Get the stack scroll of the task to anchor to (since we are removing something, the front
+        // most task will be our anchor task)
+        Task anchorTask = null;
+        float prevAnchorTaskScroll = 0;
+        boolean pullStackForward = stack.getTaskCount() > 0;
+        if (pullStackForward) {
+            anchorTask = mStack.getFrontMostTask();
+            prevAnchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
+        }
+
         // Update the min/max scroll and animate other task views into their new positions
         updateMinMaxScroll(true, mConfig.launchedWithAltTab, mConfig.launchedFromHome);
+
+        // Offset the stack by as much as the anchor task would otherwise move back
+        if (pullStackForward) {
+            float anchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
+            mStackScroller.setStackScroll(mStackScroller.getStackScroll() + (anchorTaskScroll
+                    - prevAnchorTaskScroll));
+            mStackScroller.boundScroll();
+        }
+
+        // Animate all the tasks into place
         requestSynchronizeStackViewsWithModel(200);
 
         // Update the new front most task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index 495d00b..667faa7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -202,7 +202,7 @@
     /**
      * Returns the scroll to such task top = 1f;
      */
-    float getStackScrollForTaskIndex(Task t) {
+    float getStackScrollForTask(Task t) {
         return mTaskProgressMap.get(t.key);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index 2113c68..9fbcd7f 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -59,6 +59,7 @@
 
     private boolean mAutomatic;
     private boolean mListening;
+    private boolean mExternalChange;
 
     public interface BrightnessStateChangeCallback {
         public void onBrightnessLevelChanged();
@@ -86,19 +87,24 @@
         @Override
         public void onChange(boolean selfChange, Uri uri) {
             if (selfChange) return;
-            if (BRIGHTNESS_MODE_URI.equals(uri)) {
-                updateMode();
-                updateSlider();
-            } else if (BRIGHTNESS_URI.equals(uri) && !mAutomatic) {
-                updateSlider();
-            } else if (BRIGHTNESS_ADJ_URI.equals(uri) && mAutomatic) {
-                updateSlider();
-            } else {
-                updateMode();
-                updateSlider();
-            }
-            for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
-                cb.onBrightnessLevelChanged();
+            try {
+                mExternalChange = true;
+                if (BRIGHTNESS_MODE_URI.equals(uri)) {
+                    updateMode();
+                    updateSlider();
+                } else if (BRIGHTNESS_URI.equals(uri) && !mAutomatic) {
+                    updateSlider();
+                } else if (BRIGHTNESS_ADJ_URI.equals(uri) && mAutomatic) {
+                    updateSlider();
+                } else {
+                    updateMode();
+                    updateSlider();
+                }
+                for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
+                    cb.onBrightnessLevelChanged();
+                }
+            } finally {
+                mExternalChange = false;
             }
         }
 
@@ -191,6 +197,8 @@
     @Override
     public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) {
         updateIcon(mAutomatic);
+        if (mExternalChange) return;
+
         if (!mAutomatic) {
             final int val = value + mMinimumBacklight;
             setBrightness(val);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
index d113139..a1704ff 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
@@ -40,35 +40,27 @@
         super.onCreate(savedInstanceState);
 
         final Window window = getWindow();
-        final WindowManager.LayoutParams lp = window.getAttributes();
 
-        // Offset from the top
-        lp.y = getResources().getDimensionPixelOffset(R.dimen.volume_panel_top);
-        lp.type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
-
-        window.setAttributes(lp);
         window.setGravity(Gravity.TOP);
         window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
         window.requestFeature(Window.FEATURE_NO_TITLE);
 
         setContentView(R.layout.quick_settings_brightness_dialog);
+
+        final ImageView icon = (ImageView) findViewById(R.id.brightness_icon);
+        final ToggleSlider slider = (ToggleSlider) findViewById(R.id.brightness_slider);
+        mBrightnessController = new BrightnessController(this, icon, slider);
     }
 
     @Override
     protected void onStart() {
         super.onStart();
-
-        final ImageView icon = (ImageView) findViewById(R.id.brightness_icon);
-        final ToggleSlider slider = (ToggleSlider) findViewById(R.id.brightness_slider);
-        mBrightnessController = new BrightnessController(this, icon, slider);
         mBrightnessController.registerCallbacks();
     }
 
     @Override
     protected void onStop() {
         super.onStop();
-
         mBrightnessController.unregisterCallbacks();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 99487ff..ca290e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -662,7 +662,6 @@
     }
 
     public void reset() {
-        super.reset();
         setTintColor(0);
         setShowingLegacyBackground(false);
         setBelowSpeedBump(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index f4857eb..947d70d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1220,8 +1220,6 @@
 
         row.setClearable(sbn.isClearable());
 
-        row.setDrawingCacheEnabled(true);
-
         if (MULTIUSER_DEBUG) {
             TextView debug = (TextView) row.findViewById(R.id.debug_info);
             if (debug != null) {
@@ -1737,6 +1735,9 @@
                 : null;
 
         // Reapply the RemoteViews
+        if (entry.row != null) {
+            entry.row.resetHeight();
+        }
         contentView.reapply(mContext, entry.expanded, mOnClickHandler);
         if (bigContentView != null && entry.getBigContentView() != null) {
             bigContentView.reapply(mContext, entry.getBigContentView(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 43b7707..9ac20a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -88,9 +88,14 @@
         mExpansionDisabled = false;
         mPublicLayout.reset();
         mPrivateLayout.reset();
+        resetHeight();
+        logExpansionEvent(false, wasExpanded);
+    }
+
+    public void resetHeight() {
         mMaxExpandHeight = 0;
         mWasReset = true;
-        logExpansionEvent(false, wasExpanded);
+        onHeightReset();
     }
 
     @Override
@@ -178,20 +183,26 @@
      * @param expand whether the system wants this notification to be expanded.
      */
     public void setSystemExpanded(boolean expand) {
-        final boolean wasExpanded = isExpanded();
-        mIsSystemExpanded = expand;
-        notifyHeightChanged();
-        logExpansionEvent(false, wasExpanded);
+        if (expand != mIsSystemExpanded) {
+            final boolean wasExpanded = isExpanded();
+            mIsSystemExpanded = expand;
+            notifyHeightChanged();
+            logExpansionEvent(false, wasExpanded);
+        }
     }
 
     /**
      * @param expansionDisabled whether to prevent notification expansion
      */
     public void setExpansionDisabled(boolean expansionDisabled) {
-        final boolean wasExpanded = isExpanded();
-        mExpansionDisabled = expansionDisabled;
-        logExpansionEvent(false, wasExpanded);
-        notifyHeightChanged();
+        if (expansionDisabled != mExpansionDisabled) {
+            final boolean wasExpanded = isExpanded();
+            mExpansionDisabled = expansionDisabled;
+            logExpansionEvent(false, wasExpanded);
+            if (wasExpanded != isExpanded()) {
+                notifyHeightChanged();
+            }
+        }
     }
 
     /**
@@ -368,9 +379,8 @@
         mPrivateLayout.notifyContentUpdated();
     }
 
-    public boolean isShowingLayoutLayouted() {
-        NotificationContentView showingLayout = getShowingLayout();
-        return showingLayout.getWidth() != 0;
+    public boolean isMaxExpandHeightInitialized() {
+        return mMaxExpandHeight != 0;
     }
 
     private NotificationContentView getShowingLayout() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index df64edf..127ff6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -255,8 +255,10 @@
     public void setBelowSpeedBump(boolean below) {
     }
 
-    public void reset() {
-        mOnHeightChangedListener.onReset(this);
+    public void onHeightReset() {
+        if (mOnHeightChangedListener != null) {
+            mOnHeightChangedListener.onReset(this);
+        }
     }
 
     /**
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 b8ac3e7..3e2a398 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -25,9 +25,12 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
+import android.os.AsyncTask;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.phone.PhoneManager;
 import android.provider.MediaStore;
+import android.telecomm.TelecommManager;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.TypedValue;
@@ -246,7 +249,18 @@
     }
 
     public void launchPhone() {
-        mActivityStarter.startActivity(PHONE_INTENT, false /* dismissShade */);
+        TelecommManager tm = TelecommManager.from(mContext);
+        if (tm.isInAPhoneCall()) {
+            final PhoneManager pm = (PhoneManager) mContext.getSystemService(Context.PHONE_SERVICE);
+            AsyncTask.execute(new Runnable() {
+                @Override
+                public void run() {
+                    pm.showCallScreen(false /* showDialpad */);
+                }
+            });
+        } else {
+            mActivityStarter.startActivity(PHONE_INTENT, false /* dismissShade */);
+        }
     }
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index 47325c8..d7144da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -21,10 +21,13 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.ContactsContract;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.FrameLayout;
 
+import com.android.systemui.R;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 
@@ -36,9 +39,11 @@
     private QSPanel mQsPanel;
     private KeyguardUserSwitcher mKeyguardUserSwitcher;
     private boolean mKeyguardMode;
+    final UserManager mUserManager;
 
     public MultiUserSwitch(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mUserManager = UserManager.get(getContext());
     }
 
     @Override
@@ -80,6 +85,22 @@
     }
 
     @Override
+    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+        super.onPopulateAccessibilityEvent(event);
+
+        if (isClickable()) {
+            final UserManager um = UserManager.get(getContext());
+            String text = mContext.getString(um.isUserSwitcherEnabled()
+                    ? R.string.accessibility_multi_user_switch_switcher
+                    : R.string.accessibility_multi_user_switch_quick_contact);
+            if (!TextUtils.isEmpty(text)) {
+                event.getText().add(text);
+            }
+        }
+
+    }
+
+    @Override
     public boolean hasOverlappingRendering() {
         return false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index bac46be..42ae0c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.util.AttributeSet;
+import android.util.MathUtils;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -104,7 +105,6 @@
     private boolean mQsFullyExpanded;
     private boolean mKeyguardShowing;
     private boolean mDozing;
-    private boolean mKeyguardStatusBarTransparent;
     private int mStatusBarState;
     private float mInitialHeightOnTouch;
     private float mInitialTouchX;
@@ -117,6 +117,7 @@
     private int mQsPeekHeight;
     private boolean mStackScrollerOverscrolling;
     private boolean mQsExpansionFromOverscroll;
+    private float mLastOverscroll;
     private boolean mQsExpansionEnabled = true;
     private ValueAnimator mQsExpansionAnimator;
     private FlingAnimationUtils mFlingAnimationUtils;
@@ -273,11 +274,10 @@
                 requestScrollerTopPaddingUpdate(false /* animate */);
             }
         } else {
-            if (!mStackScrollerOverscrolling) {
-                setQsExpansion(mQsMinExpansionHeight);
-            }
+            setQsExpansion(mQsMinExpansionHeight + mLastOverscroll);
             positionClockAndNotifications();
             mNotificationStackScroller.setStackHeight(getExpandedHeight());
+            updateHeader();
         }
         mNotificationStackScroller.updateIsSmallScreen(
                 mHeader.getCollapsedHeight() + mQsPeekHeight);
@@ -552,8 +552,8 @@
     }
 
     private float getQsExpansionFraction() {
-        return (mQsExpansionHeight - mQsMinExpansionHeight)
-                / (getTempQsMaxExpansion() - mQsMinExpansionHeight);
+        return Math.min(1f, (mQsExpansionHeight - mQsMinExpansionHeight)
+                / (getTempQsMaxExpansion() - mQsMinExpansionHeight));
     }
 
     @Override
@@ -602,6 +602,10 @@
                 && event.getY(event.getActionIndex()) < mStatusBarMinHeight) {
             mTwoFingerQsExpand = true;
             requestPanelHeightUpdate();
+
+            // Normally, we start listening when the panel is expanded, but here we need to start
+            // earlier so the state is already up to date when dragging down.
+            setListening(true);
         }
         super.onTouchEvent(event);
         return true;
@@ -621,6 +625,11 @@
             mInitialHeightOnTouch = mQsExpansionHeight;
             mInitialTouchY = event.getX();
             mInitialTouchX = event.getY();
+
+            // If we interrupt an expansion gesture here, make sure to update the state correctly.
+            if (mIsExpanding) {
+                onExpandingFinished();
+            }
         }
     }
 
@@ -718,6 +727,7 @@
         float rounded = amount >= 1f ? amount : 0f;
         mStackScrollerOverscrolling = rounded != 0f && isRubberbanded;
         mQsExpansionFromOverscroll = rounded != 0f;
+        mLastOverscroll = rounded;
         updateQsState();
         setQsExpansion(mQsMinExpansionHeight + rounded);
     }
@@ -984,15 +994,7 @@
         requestScrollerTopPaddingUpdate(false /* animate */);
         updateNotificationScrim(height);
         if (mKeyguardShowing) {
-            float alpha = getQsExpansionFraction();
-            alpha *= 2;
-            alpha = Math.min(1, alpha);
-            alpha = 1 - alpha;
-            mKeyguardStatusBarTransparent = alpha == 0f;
-            updateKeyguardStatusBarVisibility();
-            if (!mKeyguardStatusBarTransparent) {
-                mKeyguardStatusBar.setAlpha(alpha);
-            }
+            updateHeaderKeyguard();
         }
         if (mStatusBarState == StatusBarState.SHADE && mQsExpanded
                 && !mStackScrollerOverscrolling && mQsScrimEnabled) {
@@ -1025,7 +1027,9 @@
     }
 
     private float calculateQsTopPadding() {
-        if (mKeyguardShowing) {
+        // We can only do the smoother transition on Keyguard when we also are not collapsing from a
+        // scrolled quick settings.
+        if (mKeyguardShowing && mScrollYOverride == -1) {
             return interpolate(getQsExpansionFraction(),
                     mNotificationStackScroller.getIntrinsicPadding() - mNotificationTopPadding,
                     mQsMaxExpansionHeight);
@@ -1081,6 +1085,7 @@
             }
             return;
         }
+        mScrollView.setBlockFlinging(true);
         ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
         mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@@ -1092,6 +1097,7 @@
         animator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
+                mScrollView.setBlockFlinging(false);
                 mScrollYOverride = -1;
                 mQsExpansionAnimator = null;
                 if (onFinishRunnable != null) {
@@ -1339,27 +1345,25 @@
     }
 
     private void updateHeaderKeyguard() {
-        float alpha;
+        float alphaNotifications;
         if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
 
             // When on Keyguard, we hide the header as soon as the top card of the notification
             // stack scroller is close enough (collision distance) to the bottom of the header.
-            alpha = getNotificationsTopY()
+            alphaNotifications = getNotificationsTopY()
                     /
-                    (mQsMinExpansionHeight + mNotificationsHeaderCollideDistance);
-
+                    (mKeyguardStatusBar.getHeight() + mNotificationsHeaderCollideDistance);
         } else {
 
             // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
             // soon as we start translating the stack.
-            alpha = getNotificationsTopY() / mQsMinExpansionHeight;
+            alphaNotifications = getNotificationsTopY() / mKeyguardStatusBar.getHeight();
         }
-        alpha = Math.max(0, Math.min(alpha, 1));
-        alpha = (float) Math.pow(alpha, 0.75);
-        if (!mQsExpanded) {
-            mKeyguardStatusBar.setAlpha(alpha);
-        }
-        mKeyguardBottomArea.setAlpha(alpha);
+        alphaNotifications = MathUtils.constrain(alphaNotifications, 0, 1);
+        alphaNotifications = (float) Math.pow(alphaNotifications, 0.75);
+        float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
+        mKeyguardStatusBar.setAlpha(Math.min(alphaNotifications, alphaQsExpansion));
+        mKeyguardBottomArea.setAlpha(Math.min(1 - getQsExpansionFraction(), alphaNotifications));
         setQsTranslation(mQsExpansionHeight);
     }
 
@@ -1703,8 +1707,7 @@
     }
 
     private void updateKeyguardStatusBarVisibility() {
-        mKeyguardStatusBar.setVisibility(mKeyguardShowing && !mKeyguardStatusBarTransparent
-                && !mDozing ? VISIBLE : INVISIBLE);
+        mKeyguardStatusBar.setVisibility(mKeyguardShowing && !mDozing ? VISIBLE : INVISIBLE);
     }
 
     public void setDozing(boolean dozing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
index ee6b1a9..53361dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
@@ -33,6 +33,7 @@
     private boolean mHandlingTouchEvent;
     private float mLastX;
     private float mLastY;
+    private boolean mBlockFlinging;
 
     public ObservableScrollView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -109,6 +110,17 @@
                         maxOverScrollX, maxOverScrollY, isTouchEvent);
     }
 
+    public void setBlockFlinging(boolean blockFlinging) {
+        mBlockFlinging = blockFlinging;
+    }
+
+    @Override
+    public void fling(int velocityY) {
+        if (!mBlockFlinging) {
+            super.fling(velocityY);
+        }
+    }
+
     @Override
     protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
         super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index c8e943e..decaeb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -54,6 +54,11 @@
     private float mInitialOffsetOnTouch;
     private float mExpandedFraction = 0;
     protected float mExpandedHeight = 0;
+    private boolean mPanelClosedOnDown;
+    private boolean mHasLayoutedSinceDown;
+    private float mUpdateFlingVelocity;
+    private boolean mUpdateFlingOnLayout;
+    private boolean mTouching;
     private boolean mJustPeeked;
     private boolean mClosing;
     protected boolean mTracking;
@@ -76,7 +81,6 @@
 
     PanelBar mBar;
 
-    protected int mMaxPanelHeight = -1;
     private String mViewName;
     private float mInitialTouchY;
     private float mInitialTouchX;
@@ -226,6 +230,10 @@
                 mInitialOffsetOnTouch = mExpandedHeight;
                 mTouchSlopExceeded = false;
                 mJustPeeked = false;
+                mPanelClosedOnDown = mExpandedHeight == 0.0f;
+                mHasLayoutedSinceDown = false;
+                mUpdateFlingOnLayout = false;
+                mTouching = true;
                 if (mVelocityTracker == null) {
                     initVelocityTracker();
                 }
@@ -316,6 +324,10 @@
                     boolean expand = flingExpands(vel, vectorVel);
                     onTrackingStopped(expand);
                     fling(vel, expand);
+                    mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
+                    if (mUpdateFlingOnLayout) {
+                        mUpdateFlingVelocity = vel;
+                    }
                 } else {
                     boolean expands = onEmptySpaceClick(mInitialTouchX);
                     onTrackingStopped(expands);
@@ -325,6 +337,7 @@
                     mVelocityTracker.recycle();
                     mVelocityTracker = null;
                 }
+                mTouching = false;
                 break;
         }
         return !waitForTouchSlop || mTracking;
@@ -383,6 +396,10 @@
                 mInitialTouchX = x;
                 mTouchSlopExceeded = false;
                 mJustPeeked = false;
+                mPanelClosedOnDown = mExpandedHeight == 0.0f;
+                mHasLayoutedSinceDown = false;
+                mUpdateFlingOnLayout = false;
+                mTouching = true;
                 initVelocityTracker();
                 trackMovement(event);
                 break;
@@ -415,6 +432,10 @@
                     }
                 }
                 break;
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                mTouching = false;
+                break;
         }
         return false;
     }
@@ -444,7 +465,6 @@
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         loadDimens();
-        mMaxPanelHeight = -1;
     }
 
     /**
@@ -524,27 +544,6 @@
         return mViewName;
     }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        if (DEBUG) logf("onMeasure(%d, %d) -> (%d, %d)",
-                widthMeasureSpec, heightMeasureSpec, getMeasuredWidth(), getMeasuredHeight());
-
-        // Did one of our children change size?
-        int newHeight = getMeasuredHeight();
-        if (newHeight > mMaxPanelHeight) {
-            // we only adapt the max height if it's bigger
-            mMaxPanelHeight = newHeight;
-            // If the user isn't actively poking us, let's rubberband to the content
-            if (!mTracking && mHeightAnimator == null
-                    && mExpandedHeight > 0 && mExpandedHeight != mMaxPanelHeight
-                    && mMaxPanelHeight > 0 && mPeekAnimator == null) {
-                mExpandedHeight = mMaxPanelHeight;
-            }
-        }
-    }
-
     public void setExpandedHeight(float height) {
         if (DEBUG) logf("setExpandedHeight(%.1f)", height);
         setExpandedHeightInternal(height + getOverExpansionPixels());
@@ -552,10 +551,14 @@
 
     @Override
     protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
-        if (DEBUG) logf("onLayout: changed=%s, bottom=%d eh=%d fh=%d", changed?"T":"f", bottom,
-                (int)mExpandedHeight, mMaxPanelHeight);
         super.onLayout(changed, left, top, right, bottom);
         requestPanelHeightUpdate();
+        mHasLayoutedSinceDown = true;
+        if (mUpdateFlingOnLayout) {
+            abortAnimations();
+            fling(mUpdateFlingVelocity, true);
+            mUpdateFlingOnLayout = false;
+        }
     }
 
     protected void requestPanelHeightUpdate() {
@@ -567,7 +570,8 @@
                 && mExpandedHeight > 0
                 && currentMaxPanelHeight != mExpandedHeight
                 && !mPeekPending
-                && mPeekAnimator == null) {
+                && mPeekAnimator == null
+                && !mTouching) {
             setExpandedHeight(currentMaxPanelHeight);
         }
     }
@@ -615,10 +619,7 @@
      *
      * @return the default implementation simply returns the maximum height.
      */
-    protected int getMaxPanelHeight() {
-        mMaxPanelHeight = Math.max(mMaxPanelHeight, getHeight());
-        return mMaxPanelHeight;
-    }
+    protected abstract int getMaxPanelHeight();
 
     public void setExpandedFraction(float frac) {
         setExpandedHeight(getMaxPanelHeight() * frac);
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 e9c4822..7a9cbef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -91,6 +91,8 @@
 import android.view.ViewStub;
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.Animation;
@@ -137,6 +139,7 @@
 import com.android.systemui.statusbar.policy.FlashlightController;
 import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
 import com.android.systemui.statusbar.policy.HotspotControllerImpl;
+import com.android.systemui.statusbar.policy.KeyButtonView;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.LocationControllerImpl;
@@ -210,6 +213,9 @@
     public static final int FADE_KEYGUARD_START_DELAY = 100;
     public static final int FADE_KEYGUARD_DURATION = 300;
 
+    /** Allow some time inbetween the long press for back and recents. */
+    private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
+
     PhoneStatusBarPolicy mIconPolicy;
 
     // These are no longer handled by the policy, because we need custom strategies for them
@@ -1054,10 +1060,13 @@
         }
     };
 
-    private View.OnLongClickListener mLockToAppClickListener = new View.OnLongClickListener() {
+    private long mLastLockToAppLongPress;
+    private AccessibilityManager mAccessibilityManager;
+    private View.OnLongClickListener mLongPressBackRecentsListener =
+            new View.OnLongClickListener() {
         @Override
         public boolean onLongClick(View v) {
-            toggleLockedApp();
+            handleLongPressBackRecents(v);
             return true;
         }
     };
@@ -1106,7 +1115,9 @@
         mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
         mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
         mNavigationBarView.getRecentsButton().setLongClickable(true);
-        mNavigationBarView.getRecentsButton().setOnLongClickListener(mLockToAppClickListener);
+        mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);
+        mNavigationBarView.getBackButton().setLongClickable(true);
+        mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
         mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
         updateSearchPanel();
     }
@@ -2030,6 +2041,10 @@
         return mLeaveOpenOnKeyguardHide;
     }
 
+    public boolean isQsExpanded() {
+        return mNotificationPanel.isQsExpanded();
+    }
+
     /**
      * All changes to the status bar and notifications funnel through here and are batched.
      */
@@ -3769,15 +3784,58 @@
         mStackScroller.setAnimationsEnabled(true);
     }
 
-    public void toggleLockedApp() {
-        Log.d(TAG, "Trying to toggle lock-to-app");
+    /**
+     * This handles long-press of both back and recents.  They are
+     * handled together to capture them both being long-pressed
+     * at the same time to exit screen pinning (lock task).
+     *
+     * When accessibility mode is on, only a long-press from recents
+     * is required to exit.
+     *
+     * In all other circumstances we try to pass through long-press events
+     * for Back, so that apps can still use it.  Which can be from two things.
+     * 1) Not currently in screen pinning (lock task).
+     * 2) Back is long-pressed without recents.
+     */
+    private void handleLongPressBackRecents(View v) {
         try {
+            boolean sendBackLongPress = false;
             IActivityManager activityManager = ActivityManagerNative.getDefault();
-            if (activityManager.isInLockTaskMode()) {
-                activityManager.stopLockTaskModeOnCurrent();
+            if (mAccessibilityManager == null) {
+                mAccessibilityManager = (AccessibilityManager)
+                        mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+            }
+            boolean isAccessiblityEnabled = mAccessibilityManager.isEnabled();
+            if (activityManager.isInLockTaskMode() && !isAccessiblityEnabled) {
+                long time = System.currentTimeMillis();
+                // If we recently long-pressed the other button then they were
+                // long-pressed 'together'
+                if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
+                    activityManager.stopLockTaskModeOnCurrent();
+                } else if ((v.getId() == R.id.back)
+                        && !mNavigationBarView.getRecentsButton().isPressed()) {
+                    // If we aren't pressing recents right now then they presses
+                    // won't be together, so send the standard long-press action.
+                    sendBackLongPress = true;
+                }
+                mLastLockToAppLongPress = time;
+            } else {
+                // If this is back still need to handle sending the long-press event.
+                if (v.getId() == R.id.back) {
+                    sendBackLongPress = true;
+                } else if (isAccessiblityEnabled && activityManager.isInLockTaskMode()) {
+                    // When in accessibility mode a long press that is recents (not back)
+                    // should stop lock task.
+                    activityManager.stopLockTaskModeOnCurrent();
+                }
+            }
+            if (sendBackLongPress) {
+                KeyButtonView keyButtonView = (KeyButtonView) v;
+                keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
+                keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
             }
         } catch (RemoteException e) {
-            Log.d(TAG, "Unable to toggle Lock-to-app", e);
+            Log.d(TAG, "Unable to reach activity manager", e);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 5c9f3ca..39b2022 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -24,6 +24,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
@@ -54,7 +55,7 @@
     private View mSystemIconsSuperContainer;
     private View mDateGroup;
     private View mClock;
-    private View mTime;
+    private TextView mTime;
     private View mAmPm;
     private MultiUserSwitch mMultiUserSwitch;
     private ImageView mMultiUserAvatar;
@@ -86,6 +87,9 @@
     private int mMultiUserSwitchWidthCollapsed;
     private int mMultiUserSwitchWidthExpanded;
 
+    private int mClockCollapsedSize;
+    private int mClockExpandedSize;
+
     /**
      * In collapsed QS, the clock and avatar are scaled down a bit post-layout to allow for a nice
      * transition. These values determine that factor.
@@ -121,7 +125,7 @@
         mSystemIconsSuperContainer.setOnClickListener(this);
         mDateGroup = findViewById(R.id.date_group);
         mClock = findViewById(R.id.clock);
-        mTime = findViewById(R.id.time_view);
+        mTime = (TextView) findViewById(R.id.time_view);
         mAmPm = findViewById(R.id.am_pm_view);
         mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
         mMultiUserAvatar = (ImageView) findViewById(R.id.multi_user_avatar);
@@ -152,8 +156,6 @@
                 boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
                 mTime.setPivotX(rtl ? mTime.getWidth() : 0);
                 mTime.setPivotY(mTime.getBaseline());
-                mAmPm.setPivotX(rtl ? mAmPm.getWidth() : 0);
-                mAmPm.setPivotY(mAmPm.getBaseline());
                 updateAmPmTranslation();
             }
         });
@@ -206,9 +208,10 @@
         mAvatarCollapsedScaleFactor =
                 getResources().getDimensionPixelSize(R.dimen.multi_user_avatar_collapsed_size)
                 / (float) mMultiUserAvatar.getLayoutParams().width;
-        mClockCollapsedScaleFactor =
-                (float) getResources().getDimensionPixelSize(R.dimen.qs_time_collapsed_size)
-                / (float) getResources().getDimensionPixelSize(R.dimen.qs_time_expanded_size);
+        mClockCollapsedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_collapsed_size);
+        mClockExpandedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_expanded_size);
+        mClockCollapsedScaleFactor = (float) mClockCollapsedSize / (float) mClockExpandedSize;
+
     }
 
     public void setActivityStarter(ActivityStarter activityStarter) {
@@ -333,17 +336,14 @@
     }
 
     private void updateClockScale() {
-        mAmPm.setScaleX(mClockCollapsedScaleFactor);
-        mAmPm.setScaleY(mClockCollapsedScaleFactor);
-        mTime.setScaleX(getTimeScale());
-        mTime.setScaleY(getTimeScale());
+        mTime.setTextSize(TypedValue.COMPLEX_UNIT_PX, mExpanded
+                ? mClockExpandedSize
+                : mClockCollapsedSize);
+        mTime.setScaleX(1f);
+        mTime.setScaleY(1f);
         updateAmPmTranslation();
     }
 
-    private float getTimeScale() {
-        return !mExpanded ? mClockCollapsedScaleFactor : 1f;
-    }
-
     private void updateAmPmTranslation() {
         boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
         mAmPm.setTranslationX((rtl ? 1 : -1) * mTime.getWidth() * (1 - mTime.getScaleX()));
@@ -366,7 +366,7 @@
             mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm));
         }
         mAlarmShowing = nextAlarm != null;
-        updateVisibilities();
+        updateEverything();
         requestCaptureValues();
     }
 
@@ -512,10 +512,13 @@
     }
 
     public void setShowEmergencyCallsOnly(boolean show) {
-        mShowEmergencyCallsOnly = show;
-        if (mExpanded) {
-            updateVisibilities();
-            requestCaptureValues();
+        boolean changed = show != mShowEmergencyCallsOnly;
+        if (changed) {
+            mShowEmergencyCallsOnly = show;
+            if (mExpanded) {
+                updateEverything();
+                requestCaptureValues();
+            }
         }
     }
 
@@ -526,8 +529,8 @@
     }
 
     private void captureLayoutValues(LayoutValues target) {
-        target.timeScale = mTime.getScaleX();
-        target.clockY = mClock.getTop();
+        target.timeScale = mExpanded ? 1f : mClockCollapsedScaleFactor;
+        target.clockY = mClock.getBottom();
         target.dateY = mDateGroup.getTop();
         target.emergencyCallsOnlyAlpha = getAlphaForVisibility(mEmergencyCallsOnly);
         target.alarmStatusAlpha = getAlphaForVisibility(mAlarmStatus);
@@ -552,7 +555,7 @@
     }
 
     private void applyAlpha(View v, float alpha) {
-        if (v == null) {
+        if (v == null || v.getVisibility() == View.GONE) {
             return;
         }
         if (alpha == 0f) {
@@ -566,7 +569,7 @@
     private void applyLayoutValues(LayoutValues values) {
         mTime.setScaleX(values.timeScale);
         mTime.setScaleY(values.timeScale);
-        mClock.setY(values.clockY);
+        mClock.setY(values.clockY - mClock.getHeight());
         mDateGroup.setY(values.dateY);
         mAlarmStatus.setY(values.dateY - mAlarmStatus.getPaddingTop());
         mMultiUserAvatar.setScaleX(values.avatarScale);
@@ -595,6 +598,10 @@
         applyAlpha(mBatteryLevel, values.batteryLevelAlpha);
         applyAlpha(mSettingsButton, values.settingsAlpha);
         applyAlpha(mSignalCluster, values.signalClusterAlpha);
+        if (!mExpanded) {
+            mTime.setScaleX(1f);
+            mTime.setScaleY(1f);
+        }
         updateAmPmTranslation();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index ce1e176..f427ec4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -188,6 +188,7 @@
                             @Override
                             public void run() {
                                 mStatusBarWindowManager.setKeyguardOccluded(true);
+                                reset();
                             }
                         });
                 return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 2c5bcb79..e1fd779 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -28,7 +28,6 @@
 import android.view.ViewRootImpl;
 import android.widget.FrameLayout;
 
-import com.android.systemui.ExpandHelper;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.DragDownHelper;
@@ -122,6 +121,7 @@
             MotionEvent cancellation = MotionEvent.obtain(ev);
             cancellation.setAction(MotionEvent.ACTION_CANCEL);
             mStackScrollLayout.onInterceptTouchEvent(cancellation);
+            mNotificationPanel.onInterceptTouchEvent(cancellation);
             cancellation.recycle();
         }
         return intercept;
@@ -130,7 +130,7 @@
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         boolean handled = false;
-        if (mService.getBarState() == StatusBarState.KEYGUARD) {
+        if (mService.getBarState() == StatusBarState.KEYGUARD && !mService.isQsExpanded()) {
             handled = mDragDownHelper.onTouchEvent(ev);
         }
         if (!handled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 330b599..e9581fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -56,12 +56,12 @@
         public void run() {
             if (isPressed()) {
                 // Log.d("KeyButtonView", "longpressed: " + this);
-                if (mCode != 0) {
-                    sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
-                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
-                } else {
+                if (isLongClickable()) {
                     // Just an old-fashioned ImageView
                     performLongClick();
+                } else {
+                    sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
+                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
                 }
             }
         }
@@ -216,7 +216,7 @@
         return true;
     }
 
-    void sendEvent(int action, int flags) {
+    public void sendEvent(int action, int flags) {
         sendEvent(action, flags, SystemClock.uptimeMillis());
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
index 6c4fb7a..7d102ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.policy;
 
-import android.net.Uri;
 import android.service.notification.Condition;
 
 public interface ZenModeController {
@@ -25,15 +24,15 @@
     void setZen(int zen);
     int getZen();
     void requestConditions(boolean request);
-    void setExitConditionId(Uri exitConditionId);
-    Uri getExitConditionId();
+    void setExitCondition(Condition exitCondition);
+    Condition getExitCondition();
     long getNextAlarm();
     void setUserId(int userId);
     boolean isZenAvailable();
 
     public static class Callback {
         public void onZenChanged(int zen) {}
-        public void onExitConditionChanged(Uri exitConditionId) {}
+        public void onExitConditionChanged(Condition exitCondition) {}
         public void onConditionsChanged(Condition[] conditions) {}
         public void onNextAlarmChanged() {}
         public void onZenAvailableChanged(boolean available) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index b33e502..b0c8f26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -122,20 +122,20 @@
     }
 
     @Override
-    public void setExitConditionId(Uri exitConditionId) {
+    public void setExitCondition(Condition exitCondition) {
         try {
-            mNoMan.setZenModeCondition(exitConditionId);
+            mNoMan.setZenModeCondition(exitCondition);
         } catch (RemoteException e) {
             // noop
         }
     }
 
     @Override
-    public Uri getExitConditionId() {
+    public Condition getExitCondition() {
         try {
             final ZenModeConfig config = mNoMan.getZenModeConfig();
             if (config != null) {
-                return config.exitConditionId;
+                return config.exitCondition;
             }
         } catch (RemoteException e) {
             // noop
@@ -186,10 +186,10 @@
     }
 
     private void fireExitConditionChanged() {
-        final Uri exitConditionId = getExitConditionId();
-        if (DEBUG) Slog.d(TAG, "exitConditionId changed: " + exitConditionId);
+        final Condition exitCondition = getExitCondition();
+        if (DEBUG) Slog.d(TAG, "exitCondition changed: " + exitCondition);
         for (Callback cb : mCallbacks) {
-            cb.onExitConditionChanged(exitConditionId);
+            cb.onExitConditionChanged(exitCondition);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 462452b..1469d73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -280,7 +280,7 @@
     }
 
     private void updatePadding(boolean dimmed) {
-        mPaddingBetweenElements = dimmed
+        mPaddingBetweenElements = dimmed && mStackScrollAlgorithm.shouldScaleDimmed()
                 ? mPaddingBetweenElementsDimmed
                 : mPaddingBetweenElementsNormal;
         mBottomStackSlowDownHeight = mStackScrollAlgorithm.getBottomStackSlowDownLength();
@@ -1914,6 +1914,7 @@
     @Override
     public void onReset(ExpandableView view) {
         mRequestViewResizeAnimationOnLayout = true;
+        mStackScrollAlgorithm.onReset(view);
     }
 
     private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index f984339b3..fe855d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.stack;
 
 import android.content.Context;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -73,6 +74,7 @@
     private int mCollapseSecondCardPadding;
     private boolean mIsSmallScreen;
     private int mMaxNotificationHeight;
+    private boolean mScaleDimmed;
 
     public StackScrollAlgorithm(Context context) {
         initConstants(context);
@@ -80,7 +82,7 @@
     }
 
     private void updatePadding(boolean dimmed) {
-        mPaddingBetweenElements = dimmed
+        mPaddingBetweenElements = dimmed && mScaleDimmed
                 ? mPaddingBetweenElementsDimmed
                 : mPaddingBetweenElementsNormal;
         mTopStackTotalSize = mTopStackSlowDownLength + mPaddingBetweenElements
@@ -125,8 +127,13 @@
                 R.dimen.notification_material_rounded_rect_radius);
         mCollapseSecondCardPadding = context.getResources().getDimensionPixelSize(
                 R.dimen.notification_collapse_second_card_padding);
+        mScaleDimmed = context.getResources().getDisplayMetrics().densityDpi
+                >= DisplayMetrics.DENSITY_XXHIGH;
     }
 
+    public boolean shouldScaleDimmed() {
+        return mScaleDimmed;
+    }
 
     public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
         // The state of the local variables are saved in an algorithmState to easily subdivide it
@@ -271,7 +278,7 @@
             childViewState.dark = dark;
             childViewState.hideSensitive = hideSensitive;
             boolean isActivatedChild = activatedChild == child;
-            childViewState.scale = !dimmed || isActivatedChild
+            childViewState.scale = !mScaleDimmed || !dimmed || isActivatedChild
                     ? 1.0f
                     : DIMMED_SCALE;
             if (dimmed && activatedChild != null) {
@@ -751,39 +758,42 @@
                 // current height.
                 mFirstChildMaxHeight = mFirstChildWhileExpanding.getActualHeight();
             } else {
-
-                // We are expanding the shade, expand it to its full height.
-                if (!isMaxSizeInitialized(mFirstChildWhileExpanding)) {
-
-                    // This child was not layouted yet, wait for a layout pass
-                    mFirstChildWhileExpanding
-                            .addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
-                                @Override
-                                public void onLayoutChange(View v, int left, int top, int right,
-                                        int bottom, int oldLeft, int oldTop, int oldRight,
-                                        int oldBottom) {
-                                    if (mFirstChildWhileExpanding != null) {
-                                        mFirstChildMaxHeight = getMaxAllowedChildHeight(
-                                                mFirstChildWhileExpanding);
-                                    } else {
-                                        mFirstChildMaxHeight = 0;
-                                    }
-                                    v.removeOnLayoutChangeListener(this);
-                                }
-                            });
-                } else {
-                    mFirstChildMaxHeight = getMaxAllowedChildHeight(mFirstChildWhileExpanding);
-                }
+                updateFirstChildMaxSizeToMaxHeight();
             }
         } else {
             mFirstChildMaxHeight = 0;
         }
     }
 
+    private void updateFirstChildMaxSizeToMaxHeight() {
+        // We are expanding the shade, expand it to its full height.
+        if (!isMaxSizeInitialized(mFirstChildWhileExpanding)) {
+
+            // This child was not layouted yet, wait for a layout pass
+            mFirstChildWhileExpanding
+                    .addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+                        @Override
+                        public void onLayoutChange(View v, int left, int top, int right,
+                                int bottom, int oldLeft, int oldTop, int oldRight,
+                                int oldBottom) {
+                            if (mFirstChildWhileExpanding != null) {
+                                mFirstChildMaxHeight = getMaxAllowedChildHeight(
+                                        mFirstChildWhileExpanding);
+                            } else {
+                                mFirstChildMaxHeight = 0;
+                            }
+                            v.removeOnLayoutChangeListener(this);
+                        }
+                    });
+        } else {
+            mFirstChildMaxHeight = getMaxAllowedChildHeight(mFirstChildWhileExpanding);
+        }
+    }
+
     private boolean isMaxSizeInitialized(ExpandableView child) {
         if (child instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-            return row.isShowingLayoutLayouted();
+            return row.isMaxExpandHeightInitialized();
         }
         return child == null || child.getWidth() != 0;
     }
@@ -818,6 +828,12 @@
         updatePadding(dimmed);
     }
 
+    public void onReset(ExpandableView view) {
+        if (view.equals(mFirstChildWhileExpanding)) {
+            updateFirstChildMaxSizeToMaxHeight();
+        }
+    }
+
     class StackScrollAlgorithmState {
 
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index 5233da2..b05c242 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -112,7 +112,8 @@
 
     // Pseudo stream type for master volume
     private static final int STREAM_MASTER = -100;
-    // Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
+    // Pseudo stream type for remote volume
+    private static final int STREAM_REMOTE_MUSIC = -200;
 
     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
@@ -192,7 +193,7 @@
                 R.drawable.ic_audio_vol,
                 R.drawable.ic_audio_vol_mute,
                 false),
-        RemoteStream(AudioService.STREAM_REMOTE_MUSIC,
+        RemoteStream(STREAM_REMOTE_MUSIC,
                 R.string.volume_icon_description_media, //FIXME should have its own description
                 R.drawable.ic_media_route_on_holo_dark,
                 R.drawable.ic_media_route_disabled_holo_dark,
@@ -521,7 +522,7 @@
     private boolean isMuted(int streamType) {
         if (streamType == STREAM_MASTER) {
             return mAudioManager.isMasterMute();
-        } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
+        } else if (streamType == STREAM_REMOTE_MUSIC) {
             // TODO do we need to support a distinct mute property for remote?
             return false;
         } else {
@@ -532,7 +533,7 @@
     private int getStreamMaxVolume(int streamType) {
         if (streamType == STREAM_MASTER) {
             return mAudioManager.getMasterMaxVolume();
-        } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
+        } else if (streamType == STREAM_REMOTE_MUSIC) {
             if (mStreamControls != null) {
                 StreamControl sc = mStreamControls.get(streamType);
                 if (sc != null && sc.controller != null) {
@@ -549,7 +550,7 @@
     private int getStreamVolume(int streamType) {
         if (streamType == STREAM_MASTER) {
             return mAudioManager.getMasterVolume();
-        } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
+        } else if (streamType == STREAM_REMOTE_MUSIC) {
             if (mStreamControls != null) {
                 StreamControl sc = mStreamControls.get(streamType);
                 if (sc != null && sc.controller != null) {
@@ -564,7 +565,7 @@
     }
 
     private void setStreamVolume(StreamControl sc, int index, int flags) {
-        if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
+        if (sc.streamType == STREAM_REMOTE_MUSIC) {
             if (sc.controller != null) {
                 sc.controller.setVolumeTo(index, flags);
             } else {
@@ -690,7 +691,7 @@
     private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
         final boolean wasEnabled = sc.seekbarView.isEnabled();
         final boolean isRinger = isNotificationOrRing(sc.streamType);
-        if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
+        if (sc.streamType == STREAM_REMOTE_MUSIC) {
             // never disable touch interactions for remote playback, the muting is not tied to
             // the state of the phone.
             sc.seekbarView.setEnabled(!fixedVolume);
@@ -805,7 +806,7 @@
 
     public void postRemoteSliderVisibility(boolean visible) {
         obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
-                AudioService.STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
+                STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
     }
 
     /**
@@ -980,7 +981,7 @@
                 break;
             }
 
-            case AudioService.STREAM_REMOTE_MUSIC: {
+            case STREAM_REMOTE_MUSIC: {
                 if (controller == null && sc != null) {
                     // If we weren't passed one try using the last one set.
                     controller = sc.controller;
@@ -1003,7 +1004,7 @@
         }
 
         if (sc != null) {
-            if (streamType == AudioService.STREAM_REMOTE_MUSIC && controller != sc.controller) {
+            if (streamType == STREAM_REMOTE_MUSIC && controller != sc.controller) {
                 if (sc.controller != null) {
                     sc.controller.removeCallback(mMediaControllerCb);
                 }
@@ -1021,7 +1022,7 @@
         }
 
         if (!isShowing()) {
-            int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
+            int stream = (streamType == STREAM_REMOTE_MUSIC) ? -1 : streamType;
             // when the stream is for remote playback, use -1 to reset the stream type evaluation
             mAudioManager.forceVolumeControlStream(stream);
             mDialog.show();
@@ -1031,7 +1032,7 @@
         }
 
         // Do a little vibrate if applicable (only when going into vibrate mode)
-        if ((streamType != AudioService.STREAM_REMOTE_MUSIC) &&
+        if ((streamType != STREAM_REMOTE_MUSIC) &&
                 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
                 mAudioManager.isStreamAffectedByRingerMode(streamType) &&
                 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
@@ -1094,10 +1095,10 @@
 
         if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
             synchronized (this) {
-                if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
-                    reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
+                if (mActiveStreamType != STREAM_REMOTE_MUSIC) {
+                    reorderSliders(STREAM_REMOTE_MUSIC);
                 }
-                onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags, controller);
+                onShowVolumeChanged(STREAM_REMOTE_MUSIC, flags, controller);
             }
         } else {
             if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
@@ -1111,9 +1112,9 @@
     protected void onRemoteVolumeUpdateIfShown() {
         if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
         if (isShowing()
-                && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
+                && (mActiveStreamType == STREAM_REMOTE_MUSIC)
                 && (mStreamControls != null)) {
-            onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0, null);
+            onShowVolumeChanged(STREAM_REMOTE_MUSIC, 0, null);
         }
     }
 
@@ -1122,7 +1123,7 @@
      */
     private void clearRemoteStreamController() {
         if (mStreamControls != null) {
-            StreamControl sc = mStreamControls.get(AudioService.STREAM_REMOTE_MUSIC);
+            StreamControl sc = mStreamControls.get(STREAM_REMOTE_MUSIC);
             if (sc != null) {
                 if (sc.controller != null) {
                     sc.controller.removeCallback(mMediaControllerCb);
@@ -1133,10 +1134,10 @@
     }
 
     /**
-     * Handler for MSG_SLIDER_VISIBILITY_CHANGED
-     * Hide or show a slider
-     * @param streamType can be a valid stream type value, or VolumePanel.STREAM_MASTER,
-     *                   or AudioService.STREAM_REMOTE_MUSIC
+     * Handler for MSG_SLIDER_VISIBILITY_CHANGED Hide or show a slider
+     *
+     * @param streamType can be a valid stream type value, or
+     *            VolumePanel.STREAM_MASTER, or VolumePanel.STREAM_REMOTE_MUSIC
      * @param visible
      */
     synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 0d837c7..c99e1fd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -97,14 +97,16 @@
     private Callback mCallback;
     private ZenModeController mController;
     private boolean mRequestingConditions;
-    private Uri mExitConditionId;
+    private Condition mExitCondition;
+    private String mExitConditionText;
     private int mBucketIndex = -1;
     private boolean mExpanded;
     private boolean mHidden = false;
     private int mSessionZen;
-    private Uri mSessionExitConditionId;
-    private String mExitConditionText;
+    private Condition mSessionExitCondition;
     private long mNextAlarm;
+    private Condition[] mConditions;
+    private Condition mTimeCondition;
 
     public ZenModePanel(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -161,7 +163,7 @@
         super.onAttachedToWindow();
         if (DEBUG) Log.d(mTag, "onAttachedToWindow");
         mSessionZen = getSelectedZen(-1);
-        mSessionExitConditionId = mExitConditionId;
+        mSessionExitCondition = copy(mExitCondition);
         refreshExitConditionText();
         refreshNextAlarm();
         updateWidgets();
@@ -172,7 +174,7 @@
         super.onDetachedFromWindow();
         if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
         mSessionZen = -1;
-        mSessionExitConditionId = null;
+        mSessionExitCondition = null;
         setExpanded(false);
     }
 
@@ -199,17 +201,16 @@
             mController.requestConditions(mRequestingConditions);
         }
         if (mRequestingConditions) {
-            Condition timeCondition = parseExistingTimeCondition(mExitConditionId);
-            if (timeCondition != null) {
+            mTimeCondition = parseExistingTimeCondition(mExitCondition);
+            if (mTimeCondition != null) {
                 mBucketIndex = -1;
             } else {
                 mBucketIndex = DEFAULT_BUCKET_INDEX;
-                timeCondition = newTimeCondition(MINUTE_BUCKETS[mBucketIndex]);
+                mTimeCondition = newTimeCondition(MINUTE_BUCKETS[mBucketIndex]);
             }
             if (DEBUG) Log.d(mTag, "Initial bucket index: " + mBucketIndex);
-            handleUpdateConditions(new Condition[0]);  // ensures forever exists
-            bind(timeCondition, mZenConditions.getChildAt(TIME_CONDITION_INDEX));
-            checkForDefault();
+            mConditions = null; // reset conditions
+            handleUpdateConditions();
         } else {
             mZenConditions.removeAllViews();
         }
@@ -217,31 +218,47 @@
 
     public void init(ZenModeController controller) {
         mController = controller;
-        setExitConditionId(mController.getExitConditionId());
+        setExitCondition(mController.getExitCondition());
         refreshExitConditionText();
         mSessionZen = getSelectedZen(-1);
         handleUpdateZen(mController.getZen());
-        if (DEBUG) Log.d(mTag, "init mExitConditionId=" + mExitConditionId);
+        if (DEBUG) Log.d(mTag, "init mExitCondition=" + mExitCondition);
         mZenConditions.removeAllViews();
         mController.addCallback(mZenCallback);
     }
 
-    private void setExitConditionId(Uri exitConditionId) {
-        if (Objects.equals(mExitConditionId, exitConditionId)) return;
-        mExitConditionId = exitConditionId;
+    private void setExitCondition(Condition exitCondition) {
+        if (sameConditionId(mExitCondition, exitCondition)) return;
+        mExitCondition = exitCondition;
         refreshExitConditionText();
         updateWidgets();
     }
 
+    private Uri getExitConditionId() {
+        return getConditionId(mExitCondition);
+    }
+
+    private static Uri getConditionId(Condition condition) {
+        return condition != null ? condition.id : null;
+    }
+
+    private static boolean sameConditionId(Condition lhs, Condition rhs) {
+        return lhs == null ? rhs == null : rhs != null && lhs.id.equals(rhs.id);
+    }
+
+    private static Condition copy(Condition condition) {
+        return condition == null ? null : condition.copy();
+    }
+
     private void refreshExitConditionText() {
         final String forever = mContext.getString(R.string.zen_mode_forever);
-        if (mExitConditionId == null) {
+        if (mExitCondition == null) {
             mExitConditionText = forever;
-        } else if (ZenModeConfig.isValidCountdownConditionId(mExitConditionId)) {
-            final Condition condition = parseExistingTimeCondition(mExitConditionId);
+        } else if (ZenModeConfig.isValidCountdownConditionId(mExitCondition.id)) {
+            final Condition condition = parseExistingTimeCondition(mExitCondition);
             mExitConditionText = condition != null ? condition.summary : forever;
         } else {
-            mExitConditionText = "(until condition ends)";  // TODO persist current description
+            mExitConditionText = mExitCondition.summary;
         }
     }
 
@@ -296,7 +313,7 @@
         mZenConditions.setVisibility(!zenOff && expanded ? VISIBLE : GONE);
         mAlarmWarning.setVisibility(zenNone && expanded && hasNextAlarm ? VISIBLE : GONE);
         if (showAlarmWarning) {
-            final long exitTime = ZenModeConfig.tryParseCountdownConditionId(mExitConditionId);
+            final long exitTime = ZenModeConfig.tryParseCountdownConditionId(getExitConditionId());
             final long now = System.currentTimeMillis();
             final boolean alarmToday = time(mNextAlarm).yearDay == time(now).yearDay;
             final String skeleton = (alarmToday ? "" : "E")
@@ -330,8 +347,9 @@
         return t;
     }
 
-    private Condition parseExistingTimeCondition(Uri conditionId) {
-        final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
+    private Condition parseExistingTimeCondition(Condition condition) {
+        if (condition == null) return null;
+        final long time = ZenModeConfig.tryParseCountdownConditionId(condition.id);
         if (time == 0) return null;
         final long span = time - System.currentTimeMillis();
         if (span <= 0 || span > MAX_BUCKET_MINUTES * MINUTES_MS) return null;
@@ -365,15 +383,36 @@
     }
 
     private void handleUpdateConditions(Condition[] conditions) {
-        final int newCount = conditions == null ? 0 : conditions.length;
-        if (DEBUG) Log.d(mTag, "handleUpdateConditions newCount=" + newCount);
-        for (int i = mZenConditions.getChildCount(); i >= newCount + FIRST_CONDITION_INDEX; i--) {
+        mConditions = conditions;
+        handleUpdateConditions();
+    }
+
+    private void handleUpdateConditions() {
+        final int conditionCount = mConditions == null ? 0 : mConditions.length;
+        if (DEBUG) Log.d(mTag, "handleUpdateConditions conditionCount=" + conditionCount);
+        for (int i = mZenConditions.getChildCount() - 1; i >= FIRST_CONDITION_INDEX; i--) {
             mZenConditions.removeViewAt(i);
         }
+        // forever
         bind(null, mZenConditions.getChildAt(FOREVER_CONDITION_INDEX));
-        for (int i = 0; i < newCount; i++) {
-            bind(conditions[i], mZenConditions.getChildAt(FIRST_CONDITION_INDEX + i));
+        // countdown
+        bind(mTimeCondition, mZenConditions.getChildAt(TIME_CONDITION_INDEX));
+        // provider conditions
+        boolean foundDowntime = false;
+        for (int i = 0; i < conditionCount; i++) {
+            bind(mConditions[i], mZenConditions.getChildAt(FIRST_CONDITION_INDEX + i));
+            foundDowntime |= isDowntime(mConditions[i]);
         }
+        // ensure downtime exists, if active
+        if (isDowntime(mSessionExitCondition) && !foundDowntime) {
+            bind(mSessionExitCondition, null);
+        }
+        // ensure something is selected
+        checkForDefault();
+    }
+
+    private static boolean isDowntime(Condition c) {
+        return ZenModeConfig.isValidDowntimeConditionId(getConditionId(c));
     }
 
     private ConditionTag getConditionTagAt(int index) {
@@ -385,7 +424,7 @@
         for (int i = 0; i < mZenConditions.getChildCount(); i++) {
             if (getConditionTagAt(i).rb.isChecked()) {
                 if (DEBUG) Log.d(mTag, "Not selecting a default, checked="
-                        + getConditionTagAt(i).conditionId);
+                        + getConditionTagAt(i).condition);
                 return;
             }
         }
@@ -394,20 +433,20 @@
         if (favoriteIndex == -1) {
             getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true);
         } else {
-            final Condition c = newTimeCondition(MINUTE_BUCKETS[favoriteIndex]);
+            mTimeCondition = newTimeCondition(MINUTE_BUCKETS[favoriteIndex]);
             mBucketIndex = favoriteIndex;
-            bind(c, mZenConditions.getChildAt(TIME_CONDITION_INDEX));
+            bind(mTimeCondition, mZenConditions.getChildAt(TIME_CONDITION_INDEX));
             getConditionTagAt(TIME_CONDITION_INDEX).rb.setChecked(true);
         }
     }
 
-    private void handleExitConditionChanged(Uri exitCondition) {
-        setExitConditionId(exitCondition);
-        if (DEBUG) Log.d(mTag, "handleExitConditionChanged " + mExitConditionId);
+    private void handleExitConditionChanged(Condition exitCondition) {
+        setExitCondition(exitCondition);
+        if (DEBUG) Log.d(mTag, "handleExitConditionChanged " + mExitCondition);
         final int N = mZenConditions.getChildCount();
         for (int i = 0; i < N; i++) {
             final ConditionTag tag = getConditionTagAt(i);
-            tag.rb.setChecked(Objects.equals(tag.conditionId, exitCondition));
+            tag.rb.setChecked(sameConditionId(tag.condition, mExitCondition));
         }
     }
 
@@ -427,23 +466,23 @@
         if (tag.rb == null) {
             tag.rb = (RadioButton) row.findViewById(android.R.id.checkbox);
         }
-        tag.conditionId = condition != null ? condition.id : null;
+        tag.condition = condition;
         tag.rb.setEnabled(enabled);
-        if (mSessionExitConditionId != null && mSessionExitConditionId.equals(tag.conditionId)) {
+        if (sameConditionId(mSessionExitCondition, tag.condition)) {
             tag.rb.setChecked(true);
         }
         tag.rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
             @Override
             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                 if (mExpanded && isChecked) {
-                    if (DEBUG) Log.d(mTag, "onCheckedChanged " + tag.conditionId);
+                    if (DEBUG) Log.d(mTag, "onCheckedChanged " + tag.condition);
                     final int N = mZenConditions.getChildCount();
                     for (int i = 0; i < N; i++) {
                         ConditionTag childTag = getConditionTagAt(i);
                         if (childTag == tag) continue;
                         childTag.rb.setChecked(false);
                     }
-                    select(tag.conditionId);
+                    select(tag.condition);
                     fireInteraction();
                 }
             }
@@ -479,7 +518,7 @@
             }
         });
 
-        final long time = ZenModeConfig.tryParseCountdownConditionId(tag.conditionId);
+        final long time = ZenModeConfig.tryParseCountdownConditionId(getConditionId(tag.condition));
         if (time > 0) {
             if (mBucketIndex > -1) {
                 button1.setEnabled(mBucketIndex > 0);
@@ -504,7 +543,8 @@
         final int N = MINUTE_BUCKETS.length;
         if (mBucketIndex == -1) {
             // not on a known index, search for the next or prev bucket by time
-            final long time = ZenModeConfig.tryParseCountdownConditionId(tag.conditionId);
+            final Uri conditionId = getConditionId(tag.condition);
+            final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
             final long now = System.currentTimeMillis();
             for (int i = 0; i < N; i++) {
                 int j = up ? i : N - 1 - i;
@@ -525,24 +565,25 @@
             mBucketIndex = Math.max(0, Math.min(N - 1, mBucketIndex + (up ? 1 : -1)));
             newCondition = newTimeCondition(MINUTE_BUCKETS[mBucketIndex]);
         }
-        bind(newCondition, row);
+        mTimeCondition = newCondition;
+        bind(mTimeCondition, row);
         tag.rb.setChecked(true);
-        select(newCondition.id);
+        select(mTimeCondition);
         fireInteraction();
     }
 
-    private void select(Uri conditionId) {
-        if (DEBUG) Log.d(mTag, "select " + conditionId);
+    private void select(Condition condition) {
+        if (DEBUG) Log.d(mTag, "select " + condition);
         if (mController != null) {
-            mController.setExitConditionId(conditionId);
+            mController.setExitCondition(condition);
         }
-        setExitConditionId(conditionId);
-        if (conditionId == null) {
+        setExitCondition(condition);
+        if (condition == null) {
             mFavorites.setMinuteIndex(-1);
-        } else if (ZenModeConfig.isValidCountdownConditionId(conditionId) && mBucketIndex != -1) {
+        } else if (ZenModeConfig.isValidCountdownConditionId(condition.id) && mBucketIndex != -1) {
             mFavorites.setMinuteIndex(mBucketIndex);
         }
-        mSessionExitConditionId = conditionId;
+        mSessionExitCondition = copy(condition);
     }
 
     private void fireMoreSettings() {
@@ -574,8 +615,8 @@
         }
 
         @Override
-        public void onExitConditionChanged(Uri exitConditionId) {
-            mHandler.obtainMessage(H.EXIT_CONDITION_CHANGED, exitConditionId).sendToTarget();
+        public void onExitConditionChanged(Condition exitCondition) {
+            mHandler.obtainMessage(H.EXIT_CONDITION_CHANGED, exitCondition).sendToTarget();
         }
 
         @Override
@@ -598,9 +639,8 @@
         public void handleMessage(Message msg) {
             if (msg.what == UPDATE_CONDITIONS) {
                 handleUpdateConditions((Condition[]) msg.obj);
-                checkForDefault();
             } else if (msg.what == EXIT_CONDITION_CHANGED) {
-                handleExitConditionChanged((Uri) msg.obj);
+                handleExitConditionChanged((Condition) msg.obj);
             } else if (msg.what == UPDATE_ZEN) {
                 handleUpdateZen(msg.arg1);
             } else if (msg.what == NEXT_ALARM_CHANGED) {
@@ -618,7 +658,7 @@
     // used as the view tag on condition rows
     private static class ConditionTag {
         RadioButton rb;
-        Uri conditionId;
+        Condition condition;
     }
 
     private final class Favorites implements OnSharedPreferenceChangeListener {
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index ac76ddf..7db85f2 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -1341,9 +1341,13 @@
      * @param dimX The new size of the allocation.
      *
      * @deprecated RenderScript objects should be immutable once created.  The
-     * replacement is to create a new allocation and copy the contents.
+     * replacement is to create a new allocation and copy the contents. This
+     * function will throw an exception if API 21 or higher is used.
      */
     public synchronized void resize(int dimX) {
+        if (mRS.getApplicationContext().getApplicationInfo().targetSdkVersion >= 21) {
+            throw new RSRuntimeException("Resize is not allowed in API 21+.");
+        }
         if ((mType.getY() > 0)|| (mType.getZ() > 0) || mType.hasFaces() || mType.hasMipmaps()) {
             throw new RSInvalidStateException("Resize only support for 1D allocations at this time.");
         }
diff --git a/services/appwidget/Android.mk b/services/appwidget/Android.mk
index ca38f2f..e9bab4a 100644
--- a/services/appwidget/Android.mk
+++ b/services/appwidget/Android.mk
@@ -7,4 +7,6 @@
 LOCAL_SRC_FILES += \
       $(call all-java-files-under,java)
 
+LOCAL_JAVA_LIBRARIES := services.core
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
similarity index 100%
rename from core/java/com/android/server/SystemService.java
rename to services/core/java/com/android/server/SystemService.java
diff --git a/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
similarity index 100%
rename from core/java/com/android/server/SystemServiceManager.java
rename to services/core/java/com/android/server/SystemServiceManager.java
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ad2704a..ecd8f11 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2031,7 +2031,7 @@
 
             ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
                     "android", STOCK_PM_FLAGS);
-            mSystemThread.installSystemApplicationInfo(info);
+            mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
 
             synchronized (this) {
                 ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
diff --git a/services/core/java/com/android/server/am/LockTaskNotify.java b/services/core/java/com/android/server/am/LockTaskNotify.java
index 6f9b23d..cf65243 100644
--- a/services/core/java/com/android/server/am/LockTaskNotify.java
+++ b/services/core/java/com/android/server/am/LockTaskNotify.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.os.Handler;
 import android.os.Message;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.Toast;
 
 import com.android.internal.R;
@@ -32,9 +33,12 @@
 
     private final Context mContext;
     private final H mHandler;
+    private AccessibilityManager mAccessibilityManager;
 
     public LockTaskNotify(Context context) {
         mContext = context;
+        mAccessibilityManager = (AccessibilityManager)
+                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
         mHandler = new H();
     }
 
@@ -45,6 +49,9 @@
     public void handleShowToast(boolean isLocked) {
         String text = mContext.getString(isLocked
                 ? R.string.lock_to_app_toast_locked : R.string.lock_to_app_toast);
+        if (!isLocked && mAccessibilityManager.isEnabled()) {
+            text = mContext.getString(R.string.lock_to_app_toast_accessible);
+        }
         Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
     }
 
diff --git a/services/core/java/com/android/server/am/LockToAppRequestDialog.java b/services/core/java/com/android/server/am/LockToAppRequestDialog.java
index 0847b52..12dcf7e 100644
--- a/services/core/java/com/android/server/am/LockToAppRequestDialog.java
+++ b/services/core/java/com/android/server/am/LockToAppRequestDialog.java
@@ -13,6 +13,7 @@
 import android.provider.Settings.SettingNotFoundException;
 import android.util.Slog;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.CheckBox;
 
 import com.android.internal.R;
@@ -33,8 +34,12 @@
 
     private ILockSettings mLockSettingsService;
 
+    private AccessibilityManager mAccessibilityService;
+
     public LockToAppRequestDialog(Context context, ActivityManagerService activityManagerService) {
         mContext = context;
+        mAccessibilityService = (AccessibilityManager)
+                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
         mService = activityManagerService;
     }
 
@@ -78,7 +83,9 @@
         final int unlockStringId = getLockString(task.userId);
 
         final Resources r = Resources.getSystem();
-        final String description= r.getString(R.string.lock_to_app_description);
+        final String description= r.getString(mAccessibilityService.isEnabled()
+                ? R.string.lock_to_app_description_accessible
+                : R.string.lock_to_app_description);
         AlertDialog.Builder builder = new AlertDialog.Builder(mContext)
                         .setTitle(r.getString(R.string.lock_to_app_title))
                         .setMessage(description)
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index e549ead..f820a3c 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
 import android.media.AudioManager;
+import android.media.AudioManagerInternal;
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.media.VolumeProvider;
@@ -52,6 +53,8 @@
 import android.util.Slog;
 import android.view.KeyEvent;
 
+import com.android.server.LocalServices;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.UUID;
@@ -111,6 +114,7 @@
     // Volume handling fields
     private AudioAttributes mAudioAttrs;
     private AudioManager mAudioManager;
+    private AudioManagerInternal mAudioManagerInternal;
     private int mVolumeType = MediaSession.PLAYBACK_TYPE_LOCAL;
     private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
     private int mMaxVolume = 0;
@@ -134,6 +138,7 @@
         mService = service;
         mHandler = new MessageHandler(handler.getLooper());
         mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE);
+        mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
         mAudioAttrs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
     }
 
@@ -227,7 +232,7 @@
      *
      * @param direction The direction to adjust volume in.
      */
-    public void adjustVolume(int direction, int flags) {
+    public void adjustVolume(int direction, int flags, String packageName, int uid) {
         if (isPlaybackActive(false)) {
             flags &= ~AudioManager.FLAG_PLAY_SOUND;
         }
@@ -238,7 +243,8 @@
         }
         if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) {
             int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
-            mAudioManager.adjustStreamVolume(stream, direction, flags);
+            mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags, packageName,
+                    uid);
         } else {
             if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
                 // Nothing to do, the volume cannot be changed
@@ -262,10 +268,10 @@
         }
     }
 
-    public void setVolumeTo(int value, int flags) {
+    public void setVolumeTo(int value, int flags, String packageName, int uid) {
         if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) {
             int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
-            mAudioManager.setStreamVolume(stream, value, flags);
+            mAudioManagerInternal.setStreamVolumeForUid(stream, value, flags, packageName, uid);
         } else {
             if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
                 // Nothing to do. The volume can't be set directly.
@@ -397,6 +403,7 @@
                 return;
             }
             mDestroyed = true;
+            mHandler.post(MessageHandler.MSG_DESTROYED);
         }
     }
 
@@ -575,6 +582,29 @@
         }
     }
 
+    private void pushSessionDestroyed() {
+        synchronized (mLock) {
+            // This is the only method that may be (and can only be) called
+            // after the session is destroyed.
+            if (!mDestroyed) {
+                return;
+            }
+            for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+                ISessionControllerCallback cb = mControllerCallbacks.get(i);
+                try {
+                    cb.onSessionDestroyed();
+                } catch (DeadObjectException e) {
+                    Log.w(TAG, "Removing dead callback in pushEvent.", e);
+                    mControllerCallbacks.remove(i);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "unexpected exception in pushEvent.", e);
+                }
+            }
+            // After notifying clear all listeners
+            mControllerCallbacks.clear();
+        }
+    }
+
     private PlaybackState getStateWithUpdatedPosition() {
         PlaybackState state = mPlaybackState;
         long duration = -1;
@@ -919,6 +949,16 @@
         @Override
         public void registerCallbackListener(ISessionControllerCallback cb) {
             synchronized (mLock) {
+                // If this session is already destroyed tell the caller and
+                // don't add them.
+                if (mDestroyed) {
+                    try {
+                        cb.onSessionDestroyed();
+                    } catch (Exception e) {
+                        // ignored
+                    }
+                    return;
+                }
                 if (getControllerCbIndexForCb(cb) < 0) {
                     mControllerCallbacks.add(cb);
                     if (DEBUG) {
@@ -984,20 +1024,22 @@
         }
 
         @Override
-        public void adjustVolume(int direction, int flags) {
+        public void adjustVolume(int direction, int flags, String packageName) {
+            int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
-                MediaSessionRecord.this.adjustVolume(direction, flags);
+                MediaSessionRecord.this.adjustVolume(direction, flags, packageName, uid);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
 
         @Override
-        public void setVolumeTo(int value, int flags) {
+        public void setVolumeTo(int value, int flags, String packageName) {
+            int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
-                MediaSessionRecord.this.setVolumeTo(value, flags);
+                MediaSessionRecord.this.setVolumeTo(value, flags, packageName, uid);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1123,6 +1165,7 @@
         private static final int MSG_SEND_EVENT = 6;
         private static final int MSG_UPDATE_SESSION_STATE = 7;
         private static final int MSG_UPDATE_VOLUME = 8;
+        private static final int MSG_DESTROYED = 9;
 
         public MessageHandler(Looper looper) {
             super(looper);
@@ -1154,6 +1197,8 @@
                 case MSG_UPDATE_VOLUME:
                     pushVolumeUpdate();
                     break;
+                case MSG_DESTROYED:
+                    pushSessionDestroyed();
             }
         }
 
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 0514f48..1221aa4 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -812,7 +812,8 @@
                     Log.e(TAG, "Error adjusting default volume.", e);
                 }
             } else {
-                session.adjustVolume(direction, flags);
+                session.adjustVolume(direction, flags, getContext().getPackageName(),
+                        UserHandle.myUserId());
                 if (session.getPlaybackType() == MediaSession.PLAYBACK_TYPE_REMOTE
                         && mRvc != null) {
                     try {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index ad2bb92..bb5243c 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1060,9 +1060,11 @@
                 final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
 
                 final String baseIface = state.linkProperties.getInterfaceName();
-                connIdents.add(Pair.create(baseIface, ident));
-                if (powerSave) {
-                    connIfaces.add(baseIface);
+                if (baseIface != null) {
+                    connIdents.add(Pair.create(baseIface, ident));
+                    if (powerSave) {
+                        connIfaces.add(baseIface);
+                    }
                 }
 
                 // Stacked interfaces are considered to have same identity as
@@ -1070,9 +1072,11 @@
                 final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
                 for (LinkProperties stackedLink : stackedLinks) {
                     final String stackedIface = stackedLink.getInterfaceName();
-                    connIdents.add(Pair.create(stackedIface, ident));
-                    if (powerSave) {
-                        connIfaces.add(stackedIface);
+                    if (stackedIface != null) {
+                        connIdents.add(Pair.create(stackedIface, ident));
+                        if (powerSave) {
+                            connIfaces.add(stackedIface);
+                        }
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index e35ca46..f995dee 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -896,10 +896,12 @@
                 // Traffic occurring on the base interface is always counted for
                 // both total usage and UID details.
                 final String baseIface = state.linkProperties.getInterfaceName();
-                findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
-                findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
-                if (isMobile) {
-                    mobileIfaces.add(baseIface);
+                if (baseIface != null) {
+                    findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
+                    findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
+                    if (isMobile) {
+                        mobileIfaces.add(baseIface);
+                    }
                 }
 
                 // Traffic occurring on stacked interfaces is usually clatd,
@@ -909,15 +911,16 @@
                 final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
                 for (LinkProperties stackedLink : stackedLinks) {
                     final String stackedIface = stackedLink.getInterfaceName();
-                    findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
-                    if (isMobile) {
-                        mobileIfaces.add(stackedIface);
+                    if (stackedIface != null) {
+                        findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
+                        if (isMobile) {
+                            mobileIfaces.add(stackedIface);
+                        }
                     }
                 }
             }
         }
 
-        mobileIfaces.remove(null);
         mMobileIfaces = mobileIfaces.toArray(new String[mobileIfaces.size()]);
     }
 
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index a06daf6..189131c 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -51,8 +51,9 @@
             = new ArrayMap<IBinder, IConditionListener>();
     private final ArrayList<ConditionRecord> mRecords = new ArrayList<ConditionRecord>();
     private final CountdownConditionProvider mCountdown = new CountdownConditionProvider();
+    private final DowntimeConditionProvider mDowntime = new DowntimeConditionProvider();
 
-    private Uri mExitConditionId;
+    private Condition mExitCondition;
     private ComponentName mExitConditionComponent;
 
     public ConditionProviders(Context context, Handler handler,
@@ -97,6 +98,7 @@
             }
         }
         mCountdown.dump(pw, filter);
+        mDowntime.dump(pw, filter);
     }
 
     @Override
@@ -110,6 +112,10 @@
         mCountdown.attachBase(mContext);
         registerService(mCountdown.asInterface(), CountdownConditionProvider.COMPONENT,
                 UserHandle.USER_OWNER);
+        mDowntime.attachBase(mContext);
+        registerService(mDowntime.asInterface(), DowntimeConditionProvider.COMPONENT,
+                UserHandle.USER_OWNER);
+        mDowntime.setCallback(new DowntimeCallback());
     }
 
     @Override
@@ -125,7 +131,7 @@
             if (info.component.equals(mExitConditionComponent)) {
                 // ensure record exists, we'll wire it up and subscribe below
                 final ConditionRecord manualRecord =
-                        getRecordLocked(mExitConditionId, mExitConditionComponent);
+                        getRecordLocked(mExitCondition.id, mExitConditionComponent);
                 manualRecord.isManual = true;
             }
             final int N = mRecords.size();
@@ -149,11 +155,11 @@
             if (!r.component.equals(removed.component)) continue;
             if (r.isManual) {
                 // removing the current manual condition, exit zen
-                mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF);
+                mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "manualServiceRemoved");
             }
             if (r.isAutomatic) {
                 // removing an automatic condition, exit zen
-                mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF);
+                mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "automaticServiceRemoved");
             }
             mRecords.remove(i);
         }
@@ -249,7 +255,8 @@
                         } else if (DEBUG) {
                             Slog.d(TAG, "Exit zen: manual condition false: " + c);
                         }
-                        mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
+                        mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF,
+                                "manualConditionExit");
                         unsubscribeLocked(r);
                         r.isManual = false;
                     }
@@ -263,33 +270,46 @@
                         } else if (DEBUG) {
                             Slog.d(TAG, "Exit zen: automatic condition false: " + c);
                         }
-                        mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
+                        mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF,
+                                "automaticConditionExit");
                     } else if (c.state == Condition.STATE_TRUE) {
                         Slog.d(TAG, "Enter zen: automatic condition true: " + c);
-                        mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+                        mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                                "automaticConditionEnter");
                     }
                 }
             }
         }
     }
 
-    public void setZenModeCondition(Uri conditionId, String reason) {
-        if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId);
+    public void setZenModeCondition(Condition condition, String reason) {
+        if (DEBUG) Slog.d(TAG, "setZenModeCondition " + condition);
         synchronized(mMutex) {
             ComponentName conditionComponent = null;
-            if (ZenModeConfig.isValidCountdownConditionId(conditionId)) {
-                // constructed by the client, make sure the record exists...
-                final ConditionRecord r = getRecordLocked(conditionId,
-                        CountdownConditionProvider.COMPONENT);
-                if (r.info == null) {
-                    // ... and is associated with the in-process service
-                    r.info = checkServiceTokenLocked(mCountdown.asInterface());
+            if (condition != null) {
+                if (ZenModeConfig.isValidCountdownConditionId(condition.id)) {
+                    // constructed by the client, make sure the record exists...
+                    final ConditionRecord r = getRecordLocked(condition.id,
+                            CountdownConditionProvider.COMPONENT);
+                    if (r.info == null) {
+                        // ... and is associated with the in-process service
+                        r.info = checkServiceTokenLocked(mCountdown.asInterface());
+                    }
+                }
+                if (ZenModeConfig.isValidDowntimeConditionId(condition.id)) {
+                    // constructed by the client, make sure the record exists...
+                    final ConditionRecord r = getRecordLocked(condition.id,
+                            DowntimeConditionProvider.COMPONENT);
+                    if (r.info == null) {
+                        // ... and is associated with the in-process service
+                        r.info = checkServiceTokenLocked(mDowntime.asInterface());
+                    }
                 }
             }
             final int N = mRecords.size();
             for (int i = 0; i < N; i++) {
                 final ConditionRecord r = mRecords.get(i);
-                final boolean idEqual = r.id.equals(conditionId);
+                final boolean idEqual = condition != null && r.id.equals(condition.id);
                 if (r.isManual && !idEqual) {
                     // was previous manual condition, unsubscribe
                     unsubscribeLocked(r);
@@ -303,10 +323,10 @@
                     conditionComponent = r.component;
                 }
             }
-            if (!Objects.equals(mExitConditionId, conditionId)) {
-                mExitConditionId = conditionId;
+            if (!Objects.equals(mExitCondition, condition)) {
+                mExitCondition = condition;
                 mExitConditionComponent = conditionComponent;
-                ZenLog.traceExitCondition(mExitConditionId, mExitConditionComponent, reason);
+                ZenLog.traceExitCondition(mExitCondition, mExitConditionComponent, reason);
                 saveZenConfigLocked();
             }
         }
@@ -318,6 +338,7 @@
         RemoteException re = null;
         if (provider != null) {
             try {
+                Slog.d(TAG, "Subscribing to " + r.id + " with " + provider);
                 provider.onSubscribe(r.id);
             } catch (RemoteException e) {
                 Slog.w(TAG, "Error subscribing to " + r, e);
@@ -436,12 +457,13 @@
             return;
         }
         synchronized (mMutex) {
-            final boolean changingExit = !Objects.equals(mExitConditionId, config.exitConditionId);
-            mExitConditionId = config.exitConditionId;
+            final boolean changingExit = !Objects.equals(mExitCondition, config.exitCondition);
+            mExitCondition = config.exitCondition;
             mExitConditionComponent = config.exitConditionComponent;
             if (changingExit) {
-                ZenLog.traceExitCondition(mExitConditionId, mExitConditionComponent, "config");
+                ZenLog.traceExitCondition(mExitCondition, mExitConditionComponent, "config");
             }
+            mDowntime.setConfig(config);
             if (config.conditionComponents == null || config.conditionIds == null
                     || config.conditionComponents.length != config.conditionIds.length) {
                 if (DEBUG) Slog.d(TAG, "loadZenConfig: no conditions");
@@ -488,7 +510,7 @@
                 config.conditionIds[i] = r.id;
             }
         }
-        config.exitConditionId = mExitConditionId;
+        config.exitCondition = mExitCondition;
         config.exitConditionComponent = mExitConditionComponent;
         if (DEBUG) Slog.d(TAG, "Setting zen config to: " + config);
         mZenModeHelper.setConfig(config);
@@ -510,6 +532,26 @@
         }
     }
 
+    private class DowntimeCallback implements DowntimeConditionProvider.Callback {
+        @Override
+        public void onDowntimeChanged(boolean inDowntime) {
+            final int mode = mZenModeHelper.getZenMode();
+            final ZenModeConfig config = mZenModeHelper.getConfig();
+            // enter downtime
+            if (inDowntime && mode == Global.ZEN_MODE_OFF && config != null) {
+                final Condition condition = mDowntime.createCondition(config.toDowntimeInfo(),
+                        Condition.STATE_TRUE);
+                mZenModeHelper.setZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, "downtimeEnter");
+                setZenModeCondition(condition, "downtime");
+            }
+            // exit downtime
+            if (!inDowntime && mode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+                    && mDowntime.isDowntimeCondition(mExitCondition)) {
+                mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "downtimeExit");
+            }
+        }
+    }
+
     private static class ConditionRecord {
         public final Uri id;
         public final ComponentName component;
diff --git a/services/core/java/com/android/server/notification/CountdownConditionProvider.java b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
index aaf7cfc..37aacaa 100644
--- a/services/core/java/com/android/server/notification/CountdownConditionProvider.java
+++ b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
@@ -29,6 +29,7 @@
 import android.service.notification.IConditionProvider;
 import android.service.notification.ZenModeConfig;
 import android.text.format.DateUtils;
+import android.util.Log;
 import android.util.Slog;
 
 import com.android.server.notification.NotificationManagerService.DumpFilter;
@@ -38,8 +39,8 @@
 
 /** Built-in zen condition provider for simple time-based conditions */
 public class CountdownConditionProvider extends ConditionProviderService {
-    private static final String TAG = "CountdownConditionProvider";
-    private static final boolean DEBUG = false;
+    private static final String TAG = "CountdownConditions";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     public static final ComponentName COMPONENT =
             new ComponentName("android", CountdownConditionProvider.class.getName());
diff --git a/services/core/java/com/android/server/notification/DowntimeConditionProvider.java b/services/core/java/com/android/server/notification/DowntimeConditionProvider.java
new file mode 100644
index 0000000..317ebef
--- /dev/null
+++ b/services/core/java/com/android/server/notification/DowntimeConditionProvider.java
@@ -0,0 +1,289 @@
+/**
+ * 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.server.notification;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.service.notification.Condition;
+import android.service.notification.ConditionProviderService;
+import android.service.notification.IConditionProvider;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.DowntimeInfo;
+import android.text.format.DateFormat;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.server.notification.NotificationManagerService.DumpFilter;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Objects;
+
+/** Built-in zen condition provider for managing downtime */
+public class DowntimeConditionProvider extends ConditionProviderService {
+    private static final String TAG = "DowntimeConditions";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    public static final ComponentName COMPONENT =
+            new ComponentName("android", DowntimeConditionProvider.class.getName());
+
+    private static final String ENTER_ACTION = TAG + ".enter";
+    private static final int ENTER_CODE = 100;
+    private static final String EXIT_ACTION = TAG + ".exit";
+    private static final int EXIT_CODE = 101;
+    private static final String EXTRA_TIME = "time";
+
+    private final Calendar mCalendar = Calendar.getInstance();
+    private final Context mContext = this;
+    private final ArraySet<Integer> mDays = new ArraySet<Integer>();
+
+    private boolean mConnected;
+    private boolean mInDowntime;
+    private ZenModeConfig mConfig;
+    private Callback mCallback;
+
+    public DowntimeConditionProvider() {
+        if (DEBUG) Slog.d(TAG, "new DowntimeConditionProvider()");
+    }
+
+    public void dump(PrintWriter pw, DumpFilter filter) {
+        pw.println("    DowntimeConditionProvider:");
+        pw.print("      mConnected="); pw.println(mConnected);
+        pw.print("      mInDowntime="); pw.println(mInDowntime);
+    }
+
+    public void attachBase(Context base) {
+        attachBaseContext(base);
+    }
+
+    public IConditionProvider asInterface() {
+        return (IConditionProvider) onBind(null);
+    }
+
+    public void setCallback(Callback callback) {
+        mCallback = callback;
+    }
+
+    @Override
+    public void onConnected() {
+        if (DEBUG) Slog.d(TAG, "onConnected");
+        mConnected = true;
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(ENTER_ACTION);
+        filter.addAction(EXIT_ACTION);
+        filter.addAction(Intent.ACTION_TIME_CHANGED);
+        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+        mContext.registerReceiver(mReceiver, filter);
+        init();
+    }
+
+    @Override
+    public void onDestroy() {
+        if (DEBUG) Slog.d(TAG, "onDestroy");
+        mConnected = false;
+    }
+
+    @Override
+    public void onRequestConditions(int relevance) {
+        if (DEBUG) Slog.d(TAG, "onRequestConditions relevance=" + relevance);
+        if ((relevance & Condition.FLAG_RELEVANT_NOW) != 0) {
+            if (mInDowntime && mConfig != null) {
+                notifyCondition(createCondition(mConfig.toDowntimeInfo(), Condition.STATE_TRUE));
+            }
+        }
+    }
+
+    @Override
+    public void onSubscribe(Uri conditionId) {
+        if (DEBUG) Slog.d(TAG, "onSubscribe conditionId=" + conditionId);
+        final DowntimeInfo downtime = ZenModeConfig.tryParseDowntimeConditionId(conditionId);
+        if (downtime != null && mConfig != null) {
+            final int state = mConfig.toDowntimeInfo().equals(downtime) && mInDowntime
+                    ? Condition.STATE_TRUE : Condition.STATE_FALSE;
+            if (DEBUG) Slog.d(TAG, "notify condition state: " + Condition.stateToString(state));
+            notifyCondition(createCondition(downtime, state));
+        }
+    }
+
+    @Override
+    public void onUnsubscribe(Uri conditionId) {
+        if (DEBUG) Slog.d(TAG, "onUnsubscribe conditionId=" + conditionId);
+    }
+
+    public void setConfig(ZenModeConfig config) {
+        if (Objects.equals(mConfig, config)) return;
+        if (DEBUG) Slog.d(TAG, "setConfig");
+        mConfig = config;
+        if (mConnected) {
+            init();
+        }
+    }
+
+    public boolean isInDowntime() {
+        return mInDowntime;
+    }
+
+    public Condition createCondition(DowntimeInfo downtime, int state) {
+        if (downtime == null) return null;
+        final Uri id = ZenModeConfig.toDowntimeConditionId(downtime);
+        final String skeleton = DateFormat.is24HourFormat(mContext) ? "Hm" : "hma";
+        final Locale locale = Locale.getDefault();
+        final String pattern = DateFormat.getBestDateTimePattern(locale, skeleton);
+        final long time = getTime(System.currentTimeMillis(), downtime.endHour, downtime.endMinute);
+        final String formatted = new SimpleDateFormat(pattern, locale).format(new Date(time));
+        final String summary = mContext.getString(R.string.downtime_condition_summary, formatted);
+        return new Condition(id, summary, "", "", 0, state, Condition.FLAG_RELEVANT_NOW);
+    }
+
+    public boolean isDowntimeCondition(Condition condition) {
+        return condition != null && ZenModeConfig.isValidDowntimeConditionId(condition.id);
+    }
+
+    private void init() {
+        updateDays();
+        reevaluateDowntime();
+        updateAlarms();
+    }
+
+    private void updateDays() {
+        mDays.clear();
+        if (mConfig != null) {
+            final int[] days = ZenModeConfig.tryParseDays(mConfig.sleepMode);
+            for (int i = 0; days != null && i < days.length; i++) {
+                mDays.add(days[i]);
+            }
+        }
+    }
+
+    private boolean isInDowntime(long time) {
+        if (mConfig == null || mDays.size() == 0) return false;
+        final long start = getTime(time, mConfig.sleepStartHour, mConfig.sleepStartMinute);
+        long end = getTime(time, mConfig.sleepEndHour, mConfig.sleepEndMinute);
+        if (start == end) return false;
+        if (end < start) {
+            end = addDays(end, 1);
+        }
+        return isInDowntime(-1, time, start, end) || isInDowntime(0, time, start, end);
+    }
+
+    private boolean isInDowntime(int daysOffset, long time, long start, long end) {
+        final int day = ((getDayOfWeek(time) + daysOffset - 1) % Calendar.SATURDAY) + 1;
+        start = addDays(start, daysOffset);
+        end = addDays(end, daysOffset);
+        return mDays.contains(day) && time >= start && time < end;
+    }
+
+    private void reevaluateDowntime() {
+        final boolean inDowntime = isInDowntime(System.currentTimeMillis());
+        if (DEBUG) Slog.d(TAG, "inDowntime=" + inDowntime);
+        if (inDowntime == mInDowntime) return;
+        Slog.i(TAG, (inDowntime ? "Entering" : "Exiting" ) + " downtime");
+        mInDowntime = inDowntime;
+        ZenLog.traceDowntime(mInDowntime, getDayOfWeek(System.currentTimeMillis()), mDays);
+        fireDowntimeChanged();
+    }
+
+    private void fireDowntimeChanged() {
+        if (mCallback != null) {
+            mCallback.onDowntimeChanged(mInDowntime);
+        }
+    }
+
+    private void updateAlarms() {
+        if (mConfig == null) return;
+        updateAlarm(ENTER_ACTION, ENTER_CODE, mConfig.sleepStartHour, mConfig.sleepStartMinute);
+        updateAlarm(EXIT_ACTION, EXIT_CODE, mConfig.sleepEndHour, mConfig.sleepEndMinute);
+    }
+
+    private int getDayOfWeek(long time) {
+        mCalendar.setTimeInMillis(time);
+        return mCalendar.get(Calendar.DAY_OF_WEEK);
+    }
+
+    private long getTime(long millis, int hour, int min) {
+        mCalendar.setTimeInMillis(millis);
+        mCalendar.set(Calendar.HOUR_OF_DAY, hour);
+        mCalendar.set(Calendar.MINUTE, min);
+        mCalendar.set(Calendar.SECOND, 0);
+        mCalendar.set(Calendar.MILLISECOND, 0);
+        return mCalendar.getTimeInMillis();
+    }
+
+    private long addDays(long time, int days) {
+        mCalendar.setTimeInMillis(time);
+        mCalendar.add(Calendar.DATE, days);
+        return mCalendar.getTimeInMillis();
+    }
+
+    private void updateAlarm(String action, int requestCode, int hr, int min) {
+        final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+        final long now = System.currentTimeMillis();
+        mCalendar.setTimeInMillis(now);
+        mCalendar.set(Calendar.HOUR_OF_DAY, hr);
+        mCalendar.set(Calendar.MINUTE, min);
+        mCalendar.set(Calendar.SECOND, 0);
+        mCalendar.set(Calendar.MILLISECOND, 0);
+        long time = mCalendar.getTimeInMillis();
+        if (time <= now) {
+            time = addDays(time, 1);
+        }
+        final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, requestCode,
+                new Intent(action).putExtra(EXTRA_TIME, time), PendingIntent.FLAG_UPDATE_CURRENT);
+        alarms.cancel(pendingIntent);
+        if (mConfig.sleepMode != null) {
+            if (DEBUG) Slog.d(TAG, String.format("Scheduling %s for %s, %s in the future, now=%s",
+                    action, ts(time), time - now, ts(now)));
+            alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
+        }
+    }
+
+    private static String ts(long time) {
+        return new Date(time) + " (" + time + ")";
+    }
+
+    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            final long now = System.currentTimeMillis();
+            if (ENTER_ACTION.equals(action) || EXIT_ACTION.equals(action)) {
+                final long schTime = intent.getLongExtra(EXTRA_TIME, 0);
+                if (DEBUG) Slog.d(TAG, String.format("%s scheduled for %s, fired at %s, delta=%s",
+                        action, ts(schTime), ts(now), now - schTime));
+            } else {
+                if (DEBUG) Slog.d(TAG, action + " fired at " + now);
+            }
+            reevaluateDowntime();
+            updateAlarms();
+        }
+    };
+
+    public interface Callback {
+        void onDowntimeChanged(boolean inDowntime);
+    }
+}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 36be21f..f647037 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -41,6 +41,7 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -64,7 +65,7 @@
  */
 abstract public class ManagedServices {
     protected final String TAG = getClass().getSimpleName();
-    protected static final boolean DEBUG = true;
+    protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final String ENABLED_SERVICES_SEPARATOR = ":";
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index d6afe68..f2ac963 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1351,11 +1351,11 @@
         }
 
         @Override
-        public void setZenModeCondition(Uri conditionId) {
+        public void setZenModeCondition(Condition condition) {
             enforceSystemOrSystemUI("INotificationManager.setZenModeCondition");
             final long identity = Binder.clearCallingIdentity();
             try {
-                mConditionProviders.setZenModeCondition(conditionId, "binderCall");
+                mConditionProviders.setZenModeCondition(condition, "binderCall");
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index b22ed2d..525f5f8 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -22,8 +22,10 @@
 import android.os.Build;
 import android.os.RemoteException;
 import android.provider.Settings.Global;
+import android.service.notification.Condition;
 import android.service.notification.IConditionProvider;
 import android.service.notification.ZenModeConfig;
+import android.util.ArraySet;
 import android.util.Slog;
 
 import java.io.PrintWriter;
@@ -52,13 +54,14 @@
     private static final int TYPE_ALLOW_DISABLE = 2;
     private static final int TYPE_SET_RINGER_MODE = 3;
     private static final int TYPE_DOWNTIME = 4;
-    private static final int TYPE_ZEN_MODE = 5;
-    private static final int TYPE_EXIT_CONDITION = 6;
-    private static final int TYPE_SUBSCRIBE = 7;
-    private static final int TYPE_UNSUBSCRIBE = 8;
-    private static final int TYPE_CONFIG = 9;
-    private static final int TYPE_FOLLOW_RINGER_MODE = 10;
-    private static final int TYPE_NOT_INTERCEPTED = 11;
+    private static final int TYPE_SET_ZEN_MODE = 5;
+    private static final int TYPE_UPDATE_ZEN_MODE = 6;
+    private static final int TYPE_EXIT_CONDITION = 7;
+    private static final int TYPE_SUBSCRIBE = 8;
+    private static final int TYPE_UNSUBSCRIBE = 9;
+    private static final int TYPE_CONFIG = 10;
+    private static final int TYPE_FOLLOW_RINGER_MODE = 11;
+    private static final int TYPE_NOT_INTERCEPTED = 12;
 
     private static int sNext;
     private static int sSize;
@@ -82,17 +85,20 @@
         append(TYPE_SET_RINGER_MODE, ringerModeToString(ringerMode));
     }
 
-    public static void traceDowntime(boolean enter, int day, int[] days) {
-        append(TYPE_DOWNTIME, enter + ",day=" + day + ",days=" + (days != null ? Arrays.asList(days)
-                : null));
+    public static void traceDowntime(boolean inDowntime, int day, ArraySet<Integer> days) {
+        append(TYPE_DOWNTIME, inDowntime + ",day=" + day + ",days=" + days);
+    }
+
+    public static void traceSetZenMode(int mode, String reason) {
+        append(TYPE_SET_ZEN_MODE, zenModeToString(mode) + "," + reason);
     }
 
     public static void traceUpdateZenMode(int fromMode, int toMode) {
-        append(TYPE_ZEN_MODE, zenModeToString(fromMode) + " -> " + zenModeToString(toMode));
+        append(TYPE_UPDATE_ZEN_MODE, zenModeToString(fromMode) + " -> " + zenModeToString(toMode));
     }
 
-    public static void traceExitCondition(Uri id, ComponentName component, String reason) {
-        append(TYPE_EXIT_CONDITION, id + "," + componentToString(component) + "," + reason);
+    public static void traceExitCondition(Condition c, ComponentName component, String reason) {
+        append(TYPE_EXIT_CONDITION, c + "," + componentToString(component) + "," + reason);
     }
 
     public static void traceSubscribe(Uri uri, IConditionProvider provider, RemoteException e) {
@@ -122,7 +128,8 @@
             case TYPE_ALLOW_DISABLE: return "allow_disable";
             case TYPE_SET_RINGER_MODE: return "set_ringer_mode";
             case TYPE_DOWNTIME: return "downtime";
-            case TYPE_ZEN_MODE: return "zen_mode";
+            case TYPE_SET_ZEN_MODE: return "set_zen_mode";
+            case TYPE_UPDATE_ZEN_MODE: return "update_zen_mode";
             case TYPE_EXIT_CONDITION: return "exit_condition";
             case TYPE_SUBSCRIBE: return "subscribe";
             case TYPE_UNSUBSCRIBE: return "unsubscribe";
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 9282283..758f334 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -20,10 +20,8 @@
 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
 import static android.media.AudioAttributes.USAGE_UNKNOWN;
 
-import android.app.AlarmManager;
 import android.app.AppOpsManager;
 import android.app.Notification;
-import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -44,6 +42,7 @@
 import android.service.notification.NotificationListenerService;
 import android.service.notification.ZenModeConfig;
 import android.telecomm.TelecommManager;
+import android.util.Log;
 import android.util.Slog;
 
 import com.android.internal.R;
@@ -57,8 +56,6 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
 import java.util.Objects;
 
 /**
@@ -66,12 +63,7 @@
  */
 public class ZenModeHelper {
     private static final String TAG = "ZenModeHelper";
-
-    private static final String ACTION_ENTER_ZEN = "enter_zen";
-    private static final int REQUEST_CODE_ENTER = 100;
-    private static final String ACTION_EXIT_ZEN = "exit_zen";
-    private static final int REQUEST_CODE_EXIT = 101;
-    private static final String EXTRA_TIME = "time";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final Context mContext;
     private final Handler mHandler;
@@ -96,10 +88,8 @@
         mSettingsObserver.observe();
 
         final IntentFilter filter = new IntentFilter();
-        filter.addAction(ACTION_ENTER_ZEN);
-        filter.addAction(ACTION_EXIT_ZEN);
         filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
-        mContext.registerReceiver(new ZenBroadcastReceiver(), filter);
+        mContext.registerReceiver(mReceiver, filter);
     }
 
     public static ZenModeConfig readDefaultConfig(Resources resources) {
@@ -156,7 +146,7 @@
     public void requestFromListener(int hints) {
         final int newZen = zenFromListenerHint(hints, -1);
         if (newZen != -1) {
-            setZenMode(newZen);
+            setZenMode(newZen, "listener");
         }
     }
 
@@ -179,24 +169,19 @@
                     return false;
                 }
             }
-            // audience has veto power over all following rules
-            if (!audienceMatches(record)) {
-                ZenLog.traceIntercepted(record, "!audienceMatches");
-                return true;
-            }
             if (isCall(record)) {
                 if (!mConfig.allowCalls) {
                     ZenLog.traceIntercepted(record, "!allowCalls");
                     return true;
                 }
-                return false;
+                return shouldInterceptAudience(record);
             }
             if (isMessage(record)) {
                 if (!mConfig.allowMessages) {
                     ZenLog.traceIntercepted(record, "!allowMessages");
                     return true;
                 }
-                return false;
+                return shouldInterceptAudience(record);
             }
             ZenLog.traceIntercepted(record, "!allowed");
             return true;
@@ -204,11 +189,20 @@
         return false;
     }
 
+    private boolean shouldInterceptAudience(NotificationRecord record) {
+        if (!audienceMatches(record)) {
+            ZenLog.traceIntercepted(record, "!audienceMatches");
+            return true;
+        }
+        return false;
+    }
+
     public int getZenMode() {
         return mZenMode;
     }
 
-    public void setZenMode(int zenModeValue) {
+    public void setZenMode(int zenModeValue, String reason) {
+        ZenLog.traceSetZenMode(zenModeValue, reason);
         Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zenModeValue);
     }
 
@@ -216,9 +210,6 @@
         final int mode = Global.getInt(mContext.getContentResolver(),
                 Global.ZEN_MODE, Global.ZEN_MODE_OFF);
         if (mode != mZenMode) {
-            Slog.d(TAG, String.format("updateZenMode: %s -> %s",
-                    Global.zenModeToString(mZenMode),
-                    Global.zenModeToString(mode)));
             ZenLog.traceUpdateZenMode(mZenMode, mode);
         }
         mZenMode = mode;
@@ -255,12 +246,12 @@
             if (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
                 if (ringerMode != AudioManager.RINGER_MODE_SILENT) {
                     mPreviousRingerMode = ringerMode;
-                    Slog.d(TAG, "Silencing ringer");
+                    if (DEBUG) Slog.d(TAG, "Silencing ringer");
                     forcedRingerMode = AudioManager.RINGER_MODE_SILENT;
                 }
             } else {
                 if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
-                    Slog.d(TAG, "Unsilencing ringer");
+                    if (DEBUG) Slog.d(TAG, "Unsilencing ringer");
                     forcedRingerMode = mPreviousRingerMode != -1 ? mPreviousRingerMode
                             : AudioManager.RINGER_MODE_NORMAL;
                     mPreviousRingerMode = -1;
@@ -318,7 +309,6 @@
         dispatchOnConfigChanged();
         final String val = Integer.toString(mConfig.hashCode());
         Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
-        updateAlarms();
         updateZenMode();
         return true;
     }
@@ -339,7 +329,7 @@
             }
             if (newZen != -1) {
                 ZenLog.traceFollowRingerMode(ringerMode, mZenMode, newZen);
-                setZenMode(newZen);
+                setZenMode(newZen, "ringerMode");
             }
         }
     }
@@ -377,7 +367,7 @@
             final TelecommManager telecomm =
                     (TelecommManager) mContext.getSystemService(Context.TELECOMM_SERVICE);
             mDefaultPhoneApp = telecomm != null ? telecomm.getDefaultPhoneApp() : null;
-            Slog.d(TAG, "Default phone app: " + mDefaultPhoneApp);
+            if (DEBUG) Slog.d(TAG, "Default phone app: " + mDefaultPhoneApp);
         }
         return pkg != null && mDefaultPhoneApp != null
                 && pkg.equals(mDefaultPhoneApp.getPackageName());
@@ -409,40 +399,6 @@
         }
     }
 
-    private void updateAlarms() {
-        updateAlarm(ACTION_ENTER_ZEN, REQUEST_CODE_ENTER,
-                mConfig.sleepStartHour, mConfig.sleepStartMinute);
-        updateAlarm(ACTION_EXIT_ZEN, REQUEST_CODE_EXIT,
-                mConfig.sleepEndHour, mConfig.sleepEndMinute);
-    }
-
-    private void updateAlarm(String action, int requestCode, int hr, int min) {
-        final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
-        final long now = System.currentTimeMillis();
-        final Calendar c = Calendar.getInstance();
-        c.setTimeInMillis(now);
-        c.set(Calendar.HOUR_OF_DAY, hr);
-        c.set(Calendar.MINUTE, min);
-        c.set(Calendar.SECOND, 0);
-        c.set(Calendar.MILLISECOND, 0);
-        if (c.getTimeInMillis() <= now) {
-            c.add(Calendar.DATE, 1);
-        }
-        final long time = c.getTimeInMillis();
-        final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, requestCode,
-                new Intent(action).putExtra(EXTRA_TIME, time), PendingIntent.FLAG_UPDATE_CURRENT);
-        alarms.cancel(pendingIntent);
-        if (mConfig.sleepMode != null) {
-            Slog.d(TAG, String.format("Scheduling %s for %s, %s in the future, now=%s",
-                    action, ts(time), time - now, ts(now)));
-            alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
-        }
-    }
-
-    private static String ts(long time) {
-        return new Date(time) + " (" + time + ")";
-    }
-
     private final Runnable mRingerModeChanged = new Runnable() {
         @Override
         public void run() {
@@ -475,47 +431,12 @@
         }
     }
 
-    private class ZenBroadcastReceiver extends BroadcastReceiver {
-        private final Calendar mCalendar = Calendar.getInstance();
-
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (ACTION_ENTER_ZEN.equals(intent.getAction())) {
-                setZenMode(intent, Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
-            } else if (ACTION_EXIT_ZEN.equals(intent.getAction())) {
-                setZenMode(intent, Global.ZEN_MODE_OFF);
-            } else if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(intent.getAction())) {
-                mHandler.post(mRingerModeChanged);
-            }
+            mHandler.post(mRingerModeChanged);
         }
-
-        private void setZenMode(Intent intent, int zenModeValue) {
-            final long schTime = intent.getLongExtra(EXTRA_TIME, 0);
-            final long now = System.currentTimeMillis();
-            Slog.d(TAG, String.format("%s scheduled for %s, fired at %s, delta=%s",
-                    intent.getAction(), ts(schTime), ts(now), now - schTime));
-
-            final int[] days = ZenModeConfig.tryParseDays(mConfig.sleepMode);
-            boolean enter = false;
-            final int day = getDayOfWeek(schTime);
-            if (days != null) {
-                for (int i = 0; i < days.length; i++) {
-                    if (days[i] == day) {
-                        enter = true;
-                        ZenModeHelper.this.setZenMode(zenModeValue);
-                        break;
-                    }
-                }
-            }
-            ZenLog.traceDowntime(enter, day, days);
-            updateAlarms();
-        }
-
-        private int getDayOfWeek(long time) {
-            mCalendar.setTimeInMillis(time);
-            return mCalendar.get(Calendar.DAY_OF_WEEK);
-        }
-    }
+    };
 
     public static class Callback {
         void onConfigChanged() {}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index b261ef5..d1e03ec 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -16,33 +16,23 @@
 
 package com.android.server.pm;
 
-import com.android.server.SystemService;
-
 import android.content.Context;
 import android.content.pm.PackageStats;
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
+import android.os.Build;
 import android.util.Slog;
+import dalvik.system.VMRuntime;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.List;
+import com.android.internal.os.InstallerConnection;
+import com.android.server.SystemService;
 
 public final class Installer extends SystemService {
     private static final String TAG = "Installer";
 
-    private static final boolean LOCAL_DEBUG = false;
-
-    InputStream mIn;
-    OutputStream mOut;
-    LocalSocket mSocket;
-
-    byte buf[] = new byte[1024];
-    int buflen = 0;
+    private final InstallerConnection mInstaller;
 
     public Installer(Context context) {
         super(context);
+        mInstaller = new InstallerConnection();
     }
 
     @Override
@@ -51,154 +41,6 @@
         ping();
     }
 
-    private boolean connect() {
-        if (mSocket != null) {
-            return true;
-        }
-        Slog.i(TAG, "connecting...");
-        try {
-            mSocket = new LocalSocket();
-
-            LocalSocketAddress address = new LocalSocketAddress("installd",
-                    LocalSocketAddress.Namespace.RESERVED);
-
-            mSocket.connect(address);
-
-            mIn = mSocket.getInputStream();
-            mOut = mSocket.getOutputStream();
-        } catch (IOException ex) {
-            disconnect();
-            return false;
-        }
-        return true;
-    }
-
-    private void disconnect() {
-        Slog.i(TAG, "disconnecting...");
-        try {
-            if (mSocket != null)
-                mSocket.close();
-        } catch (IOException ex) {
-        }
-        try {
-            if (mIn != null)
-                mIn.close();
-        } catch (IOException ex) {
-        }
-        try {
-            if (mOut != null)
-                mOut.close();
-        } catch (IOException ex) {
-        }
-        mSocket = null;
-        mIn = null;
-        mOut = null;
-    }
-
-    private boolean readBytes(byte buffer[], int len) {
-        int off = 0, count;
-        if (len < 0)
-            return false;
-        while (off != len) {
-            try {
-                count = mIn.read(buffer, off, len - off);
-                if (count <= 0) {
-                    Slog.e(TAG, "read error " + count);
-                    break;
-                }
-                off += count;
-            } catch (IOException ex) {
-                Slog.e(TAG, "read exception");
-                break;
-            }
-        }
-        if (LOCAL_DEBUG) {
-            Slog.i(TAG, "read " + len + " bytes");
-        }
-        if (off == len)
-            return true;
-        disconnect();
-        return false;
-    }
-
-    private boolean readReply() {
-        int len;
-        buflen = 0;
-        if (!readBytes(buf, 2))
-            return false;
-        len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
-        if ((len < 1) || (len > 1024)) {
-            Slog.e(TAG, "invalid reply length (" + len + ")");
-            disconnect();
-            return false;
-        }
-        if (!readBytes(buf, len))
-            return false;
-        buflen = len;
-        return true;
-    }
-
-    private boolean writeCommand(String _cmd) {
-        byte[] cmd = _cmd.getBytes();
-        int len = cmd.length;
-        if ((len < 1) || (len > 1024))
-            return false;
-        buf[0] = (byte) (len & 0xff);
-        buf[1] = (byte) ((len >> 8) & 0xff);
-        try {
-            mOut.write(buf, 0, 2);
-            mOut.write(cmd, 0, len);
-        } catch (IOException ex) {
-            Slog.e(TAG, "write error");
-            disconnect();
-            return false;
-        }
-        return true;
-    }
-
-    private synchronized String transaction(String cmd) {
-        if (!connect()) {
-            Slog.e(TAG, "connection failed");
-            return "-1";
-        }
-
-        if (!writeCommand(cmd)) {
-            /*
-             * If installd died and restarted in the background (unlikely but
-             * possible) we'll fail on the next write (this one). Try to
-             * reconnect and write the command one more time before giving up.
-             */
-            Slog.e(TAG, "write command failed? reconnect!");
-            if (!connect() || !writeCommand(cmd)) {
-                return "-1";
-            }
-        }
-        if (LOCAL_DEBUG) {
-            Slog.i(TAG, "send: '" + cmd + "'");
-        }
-        if (readReply()) {
-            String s = new String(buf, 0, buflen);
-            if (LOCAL_DEBUG) {
-                Slog.i(TAG, "recv: '" + s + "'");
-            }
-            return s;
-        } else {
-            if (LOCAL_DEBUG) {
-                Slog.i(TAG, "fail");
-            }
-            return "-1";
-        }
-    }
-
-    private int execute(String cmd) {
-        String res = transaction(cmd);
-        try {
-            return Integer.parseInt(res);
-        } catch (NumberFormatException ex) {
-            return -1;
-        }
-    }
-
     public int install(String name, int uid, int gid, String seinfo) {
         StringBuilder builder = new StringBuilder("install");
         builder.append(' ');
@@ -209,11 +51,16 @@
         builder.append(gid);
         builder.append(' ');
         builder.append(seinfo != null ? seinfo : "!");
-        return execute(builder.toString());
+        return mInstaller.execute(builder.toString());
     }
 
     public int patchoat(String apkPath, int uid, boolean isPublic, String pkgName,
             String instructionSet) {
+        if (!isValidInstructionSet(instructionSet)) {
+            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+            return -1;
+        }
+
         StringBuilder builder = new StringBuilder("patchoat");
         builder.append(' ');
         builder.append(apkPath);
@@ -224,37 +71,34 @@
         builder.append(pkgName);
         builder.append(' ');
         builder.append(instructionSet);
-        return execute(builder.toString());
+        return mInstaller.execute(builder.toString());
     }
 
     public int patchoat(String apkPath, int uid, boolean isPublic, String instructionSet) {
-        StringBuilder builder = new StringBuilder("patchoat");
-        builder.append(' ');
-        builder.append(apkPath);
-        builder.append(' ');
-        builder.append(uid);
-        builder.append(isPublic ? " 1" : " 0");
-        builder.append(" *");         // No pkgName arg present
-        builder.append(' ');
-        builder.append(instructionSet);
-        return execute(builder.toString());
+        if (!isValidInstructionSet(instructionSet)) {
+            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+            return -1;
+        }
+
+        return mInstaller.patchoat(apkPath, uid, isPublic, instructionSet);
     }
 
     public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) {
-        StringBuilder builder = new StringBuilder("dexopt");
-        builder.append(' ');
-        builder.append(apkPath);
-        builder.append(' ');
-        builder.append(uid);
-        builder.append(isPublic ? " 1" : " 0");
-        builder.append(" *");         // No pkgName arg present
-        builder.append(' ');
-        builder.append(instructionSet);
-        return execute(builder.toString());
+        if (!isValidInstructionSet(instructionSet)) {
+            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+            return -1;
+        }
+
+        return mInstaller.dexopt(apkPath, uid, isPublic, instructionSet);
     }
 
     public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
             String instructionSet) {
+        if (!isValidInstructionSet(instructionSet)) {
+            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+            return -1;
+        }
+
         StringBuilder builder = new StringBuilder("dexopt");
         builder.append(' ');
         builder.append(apkPath);
@@ -265,7 +109,7 @@
         builder.append(pkgName);
         builder.append(' ');
         builder.append(instructionSet);
-        return execute(builder.toString());
+        return mInstaller.execute(builder.toString());
     }
 
     public int idmap(String targetApkPath, String overlayApkPath, int uid) {
@@ -276,10 +120,15 @@
         builder.append(overlayApkPath);
         builder.append(' ');
         builder.append(uid);
-        return execute(builder.toString());
+        return mInstaller.execute(builder.toString());
     }
 
     public int movedex(String srcPath, String dstPath, String instructionSet) {
+        if (!isValidInstructionSet(instructionSet)) {
+            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+            return -1;
+        }
+
         StringBuilder builder = new StringBuilder("movedex");
         builder.append(' ');
         builder.append(srcPath);
@@ -287,16 +136,21 @@
         builder.append(dstPath);
         builder.append(' ');
         builder.append(instructionSet);
-        return execute(builder.toString());
+        return mInstaller.execute(builder.toString());
     }
 
     public int rmdex(String codePath, String instructionSet) {
+        if (!isValidInstructionSet(instructionSet)) {
+            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+            return -1;
+        }
+
         StringBuilder builder = new StringBuilder("rmdex");
         builder.append(' ');
         builder.append(codePath);
         builder.append(' ');
         builder.append(instructionSet);
-        return execute(builder.toString());
+        return mInstaller.execute(builder.toString());
     }
 
     public int remove(String name, int userId) {
@@ -305,7 +159,7 @@
         builder.append(name);
         builder.append(' ');
         builder.append(userId);
-        return execute(builder.toString());
+        return mInstaller.execute(builder.toString());
     }
 
     public int rename(String oldname, String newname) {
@@ -314,7 +168,7 @@
         builder.append(oldname);
         builder.append(' ');
         builder.append(newname);
-        return execute(builder.toString());
+        return mInstaller.execute(builder.toString());
     }
 
     public int fixUid(String name, int uid, int gid) {
@@ -325,7 +179,7 @@
         builder.append(uid);
         builder.append(' ');
         builder.append(gid);
-        return execute(builder.toString());
+        return mInstaller.execute(builder.toString());
     }
 
     public int deleteCacheFiles(String name, int userId) {
@@ -334,7 +188,7 @@
         builder.append(name);
         builder.append(' ');
         builder.append(userId);
-        return execute(builder.toString());
+        return mInstaller.execute(builder.toString());
     }
 
     public int deleteCodeCacheFiles(String name, int userId) {
@@ -343,7 +197,7 @@
         builder.append(name);
         builder.append(' ');
         builder.append(userId);
-        return execute(builder.toString());
+        return mInstaller.execute(builder.toString());
     }
 
     public int createUserData(String name, int uid, int userId, String seinfo) {
@@ -356,21 +210,21 @@
         builder.append(userId);
         builder.append(' ');
         builder.append(seinfo != null ? seinfo : "!");
-        return execute(builder.toString());
+        return mInstaller.execute(builder.toString());
     }
 
     public int createUserConfig(int userId) {
         StringBuilder builder = new StringBuilder("mkuserconfig");
         builder.append(' ');
         builder.append(userId);
-        return execute(builder.toString());
+        return mInstaller.execute(builder.toString());
     }
 
     public int removeUserDataDirs(int userId) {
         StringBuilder builder = new StringBuilder("rmuser");
         builder.append(' ');
         builder.append(userId);
-        return execute(builder.toString());
+        return mInstaller.execute(builder.toString());
     }
 
     public int clearUserData(String name, int userId) {
@@ -379,11 +233,11 @@
         builder.append(name);
         builder.append(' ');
         builder.append(userId);
-        return execute(builder.toString());
+        return mInstaller.execute(builder.toString());
     }
 
     public boolean ping() {
-        if (execute("ping") < 0) {
+        if (mInstaller.execute("ping") < 0) {
             return false;
         } else {
             return true;
@@ -391,18 +245,25 @@
     }
 
     public int pruneDexCache(String cacheSubDir) {
-        return execute("prunedexcache " + cacheSubDir);
+        return mInstaller.execute("prunedexcache " + cacheSubDir);
     }
 
     public int freeCache(long freeStorageSize) {
         StringBuilder builder = new StringBuilder("freecache");
         builder.append(' ');
         builder.append(String.valueOf(freeStorageSize));
-        return execute(builder.toString());
+        return mInstaller.execute(builder.toString());
     }
 
     public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath,
             String fwdLockApkPath, String asecPath, String[] instructionSets, PackageStats pStats) {
+        for (String instructionSet : instructionSets) {
+            if (!isValidInstructionSet(instructionSet)) {
+                Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+                return -1;
+            }
+        }
+
         StringBuilder builder = new StringBuilder("getsize");
         builder.append(' ');
         builder.append(pkgName);
@@ -423,7 +284,7 @@
         // just the primary.
         builder.append(instructionSets[0]);
 
-        String s = transaction(builder.toString());
+        String s = mInstaller.transact(builder.toString());
         String res[] = s.split(" ");
 
         if ((res == null) || (res.length != 5)) {
@@ -441,7 +302,7 @@
     }
 
     public int moveFiles() {
-        return execute("movefiles");
+        return mInstaller.execute("movefiles");
     }
 
     /**
@@ -467,7 +328,7 @@
         builder.append(' ');
         builder.append(userId);
 
-        return execute(builder.toString());
+        return mInstaller.execute(builder.toString());
     }
 
     public boolean restoreconData(String pkgName, String seinfo, int uid) {
@@ -478,6 +339,23 @@
         builder.append(seinfo != null ? seinfo : "!");
         builder.append(' ');
         builder.append(uid);
-        return (execute(builder.toString()) == 0);
+        return (mInstaller.execute(builder.toString()) == 0);
+    }
+
+    /**
+     * Returns true iff. {@code instructionSet} is a valid instruction set.
+     */
+    private static boolean isValidInstructionSet(String instructionSet) {
+        if (instructionSet == null) {
+            return false;
+        }
+
+        for (String abi : Build.SUPPORTED_ABIS) {
+            if (instructionSet.equals(VMRuntime.getInstructionSet(abi))) {
+                return true;
+            }
+        }
+
+        return false;
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 89bd1d4..304441c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1394,16 +1394,27 @@
              * list of process files because dexopt will have been run
              * if necessary during zygote startup.
              */
-            String bootClassPath = System.getProperty("java.boot.class.path");
+            final String bootClassPath = System.getenv("BOOTCLASSPATH");
+            final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
+
             if (bootClassPath != null) {
-                String[] paths = splitString(bootClassPath, ':');
-                for (int i=0; i<paths.length; i++) {
-                    alreadyDexOpted.add(paths[i]);
+                String[] bootClassPathElements = splitString(bootClassPath, ':');
+                for (String element : bootClassPathElements) {
+                    alreadyDexOpted.add(element);
                 }
             } else {
                 Slog.w(TAG, "No BOOTCLASSPATH found!");
             }
 
+            if (systemServerClassPath != null) {
+                String[] systemServerClassPathElements = splitString(systemServerClassPath, ':');
+                for (String element : systemServerClassPathElements) {
+                    alreadyDexOpted.add(element);
+                }
+            } else {
+                Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");
+            }
+
             boolean didDexOptLibraryOrTool = false;
 
             final List<String> allInstructionSets = getAllInstructionSets();
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 39c6e0e..db19285 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -58,8 +58,6 @@
 import android.os.SystemService;
 import android.os.UserHandle;
 import android.os.WorkSource;
-import android.os.Parcel;
-import android.os.ServiceManager;
 import android.provider.Settings;
 import android.service.dreams.DreamManagerInternal;
 import android.util.EventLog;
@@ -709,7 +707,6 @@
         if (mLowPowerModeEnabled != lowPowerModeEnabled) {
             mLowPowerModeEnabled = lowPowerModeEnabled;
             powerHintInternal(POWER_HINT_LOW_POWER_MODE, lowPowerModeEnabled ? 1 : 0);
-            setSurfaceFlingerLowPowerMode(lowPowerModeEnabled ? 1 : 0);
             mLowPowerModeEnabled = lowPowerModeEnabled;
             BackgroundThread.getHandler().post(new Runnable() {
                 @Override
@@ -2198,21 +2195,6 @@
         nativeSendPowerHint(hintId, data);
     }
 
-    private static void setSurfaceFlingerLowPowerMode(int enabled) {
-        try {
-            final IBinder flinger = ServiceManager.getService("SurfaceFlinger");
-            if (flinger != null) {
-                final Parcel data = Parcel.obtain();
-                data.writeInterfaceToken("android.ui.ISurfaceComposer");
-                data.writeInt(enabled);
-                flinger.transact(1016, data, null, 0);
-                data.recycle();
-            }
-        } catch (RemoteException ex) {
-            Slog.e(TAG, "Failed to reduce refresh rate", ex);
-        }
-    }
-
     /**
      * Low-level function turn the device off immediately, without trying
      * to be clean.  Most people should use {@link ShutdownThread} for a clean shutdown.
diff --git a/services/core/java/com/android/server/tv/PersistentDataStore.java b/services/core/java/com/android/server/tv/PersistentDataStore.java
index 4af8f2c..05a2bde 100644
--- a/services/core/java/com/android/server/tv/PersistentDataStore.java
+++ b/services/core/java/com/android/server/tv/PersistentDataStore.java
@@ -20,6 +20,7 @@
 import android.content.Intent;
 import android.media.tv.TvContentRating;
 import android.media.tv.TvInputManager;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.UserHandle;
 import android.text.TextUtils;
@@ -81,8 +82,13 @@
 
     public PersistentDataStore(Context context, int userId) {
         mContext = context;
-        mAtomicFile = new AtomicFile(new File("/data/system/tv/" + userId
-                + "/tv-input-manager-state.xml"));
+        File userDir = Environment.getUserSystemDirectory(userId);
+        if (!userDir.exists()) {
+            if (!userDir.mkdirs()) {
+                throw new IllegalStateException("User dir cannot be created: " + userDir);
+            }
+        }
+        mAtomicFile = new AtomicFile(new File(userDir, "tv-input-manager-state.xml"));
     }
 
     public boolean isParentalControlsEnabled() {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 3fcd067..b2575e6 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -252,6 +252,15 @@
         return false;
     }
 
+    void removeAllWindows() {
+        for (int winNdx = allAppWindows.size() - 1; winNdx >= 0; --winNdx) {
+            WindowState win = allAppWindows.get(winNdx);
+            if (WindowManagerService.DEBUG_WINDOW_MOVEMENT) Slog.w(WindowManagerService.TAG,
+                    "removeAllWindows: removing win=" + win);
+            win.mService.removeWindowLocked(win.mSession, win);
+        }
+    }
+
     @Override
     void dump(PrintWriter pw, String prefix) {
         super.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index dfb1200..238c77e 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -155,7 +155,7 @@
                 final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
                 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
                     final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
-                    if (winAnimator.isAnimating() && !winAnimator.isDummyAnimation()) {
+                    if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) {
                         return true;
                     }
                 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 711cf9c..2295656 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3592,7 +3592,7 @@
                 return;
             }
             final Task oldTask = mTaskIdToTask.get(atoken.groupId);
-            removeAppFromTaskLocked(atoken);
+            oldTask.removeAppToken(atoken);
 
             atoken.groupId = groupId;
             Task newTask = mTaskIdToTask.get(groupId);
@@ -4634,6 +4634,8 @@
     }
 
     void removeAppFromTaskLocked(AppWindowToken wtoken) {
+        wtoken.removeAllWindows();
+
         final Task task = mTaskIdToTask.get(wtoken.groupId);
         if (task != null) {
             if (!task.removeAppToken(wtoken)) {
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index ce2ca9b..39b70a8 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -41,6 +41,8 @@
 int register_android_server_hdmi_HdmiMhlController(JNIEnv* env);
 int register_android_server_tv_TvInputHal(JNIEnv* env);
 int register_android_server_PersistentDataBlockService(JNIEnv* env);
+int register_android_server_fingerprint_FingerprintService(JNIEnv* env);
+int register_android_server_Watchdog(JNIEnv* env);
 };
 
 using namespace android;
@@ -77,6 +79,8 @@
     register_android_server_hdmi_HdmiMhlController(env);
     register_android_server_tv_TvInputHal(env);
     register_android_server_PersistentDataBlockService(env);
+    register_android_server_fingerprint_FingerprintService(env);
+    register_android_server_Watchdog(env);
 
     return JNI_VERSION_1_4;
 }
diff --git a/services/devicepolicy/Android.mk b/services/devicepolicy/Android.mk
index a55d138..7020f17 100644
--- a/services/devicepolicy/Android.mk
+++ b/services/devicepolicy/Android.mk
@@ -7,6 +7,6 @@
 LOCAL_SRC_FILES += \
       $(call all-java-files-under,java)
 
-LOCAL_JAVA_LIBRARIES := conscrypt
+LOCAL_JAVA_LIBRARIES := conscrypt services.core
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index fc96991..d46ae42 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
 
 import android.app.admin.DevicePolicyManagerInternal;
+
 import com.android.internal.R;
 import com.android.internal.os.storage.ExternalStorageFormatter;
 import com.android.internal.util.FastXmlSerializer;
@@ -58,6 +59,7 @@
 import android.net.Uri;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.database.ContentObserver;
+import android.hardware.usb.UsbManager;
 import android.net.ProxyInfo;
 import android.os.Binder;
 import android.os.Bundle;
@@ -3417,8 +3419,7 @@
         synchronized (this) {
             long ident = Binder.clearCallingIdentity();
             try {
-                mUserManager.setUserRestrictions(new Bundle(),
-                        new UserHandle(UserHandle.USER_OWNER));
+                clearUserRestrictions(new UserHandle(UserHandle.USER_OWNER));
                 if (mDeviceOwner != null) {
                     mDeviceOwner.clearDeviceOwner();
                     mDeviceOwner.writeOwnerFile();
@@ -3481,7 +3482,7 @@
         synchronized (this) {
             long ident = Binder.clearCallingIdentity();
             try {
-                mUserManager.setUserRestrictions(new Bundle(), callingUser);
+                clearUserRestrictions(callingUser);
                 if (mDeviceOwner != null) {
                     mDeviceOwner.removeProfileOwner(callingUser.getIdentifier());
                     mDeviceOwner.writeOwnerFile();
@@ -3492,6 +3493,19 @@
         }
     }
 
+    private void clearUserRestrictions(UserHandle userHandle) {
+        AudioManager audioManager =
+                (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        Bundle userRestrictions = mUserManager.getUserRestrictions();
+        mUserManager.setUserRestrictions(new Bundle(), userHandle);
+        if (userRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME)) {
+            audioManager.setMasterMute(false);
+        }
+        if (userRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE)) {
+            audioManager.setMicrophoneMute(false);
+        }
+    }
+
     @Override
     public boolean hasUserSetupCompleted() {
         if (!mHasFeature) {
@@ -4034,7 +4048,57 @@
 
             long id = Binder.clearCallingIdentity();
             try {
+                AudioManager audioManager =
+                        (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+                boolean alreadyRestricted = mUserManager.hasUserRestriction(key);
+
+                if (enabled && !alreadyRestricted) {
+                    if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) {
+                        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                                Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0,
+                                userHandle.getIdentifier());
+                    } else if (UserManager.DISALLOW_USB_FILE_TRANSFER.equals(key)) {
+                        UsbManager manager =
+                                (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
+                        manager.setCurrentFunction("none", false);
+                    } else if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) {
+                        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                                Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF,
+                                userHandle.getIdentifier());
+                        Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                                Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "",
+                                userHandle.getIdentifier());
+                    } else if (UserManager.DISALLOW_DEBUGGING_FEATURES.equals(key)) {
+                        Settings.Global.putStringForUser(mContext.getContentResolver(),
+                                Settings.Global.ADB_ENABLED, "0", userHandle.getIdentifier());
+                    } else if (UserManager.ENSURE_VERIFY_APPS.equals(key)) {
+                        Settings.Global.putStringForUser(mContext.getContentResolver(),
+                                Settings.Global.PACKAGE_VERIFIER_ENABLE, "1",
+                                userHandle.getIdentifier());
+                        Settings.Global.putStringForUser(mContext.getContentResolver(),
+                                Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, "1",
+                                userHandle.getIdentifier());
+                    } else if (UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES.equals(key)) {
+                        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                                Settings.Secure.INSTALL_NON_MARKET_APPS, 0,
+                                userHandle.getIdentifier());
+                    } else if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
+                        audioManager.setMicrophoneMute(true);
+                    } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
+                        audioManager.setMasterMute(true);
+                    }
+                }
+
                 mUserManager.setUserRestriction(key, enabled, userHandle);
+
+                if (!enabled && alreadyRestricted) {
+                    if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
+                        audioManager.setMicrophoneMute(false);
+                    } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
+                        audioManager.setMasterMute(false);
+                    }
+                }
+
             } finally {
                 restoreCallingIdentity(id);
             }
diff --git a/services/print/Android.mk b/services/print/Android.mk
index 33604b7..00eb2e4 100644
--- a/services/print/Android.mk
+++ b/services/print/Android.mk
@@ -7,4 +7,6 @@
 LOCAL_SRC_FILES += \
       $(call all-java-files-under,java)
 
+LOCAL_JAVA_LIBRARIES := services.core
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/restrictions/Android.mk b/services/restrictions/Android.mk
index fcf8626..57d1c46 100644
--- a/services/restrictions/Android.mk
+++ b/services/restrictions/Android.mk
@@ -7,4 +7,6 @@
 LOCAL_SRC_FILES += \
       $(call all-java-files-under,java)
 
+LOCAL_JAVA_LIBRARIES := services.core
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/usage/Android.mk b/services/usage/Android.mk
index d4b7fa8..f1cbe98 100644
--- a/services/usage/Android.mk
+++ b/services/usage/Android.mk
@@ -7,4 +7,6 @@
 LOCAL_SRC_FILES += \
       $(call all-java-files-under,java)
 
+LOCAL_JAVA_LIBRARIES := services.core
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index bcbae60..5860fc7 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -314,14 +314,22 @@
         mConnectedUsbCard = cardsParser.getNumCardRecords() - 1;
         mConnectedUsbDeviceNum = 0;
 
-        if (!waitForAlsaFile(mConnectedUsbCard, mConnectedUsbDeviceNum, false)) {
-            return;
-        }
-
         mConnectedHasPlayback = devicesParser.hasPlaybackDevices(mConnectedUsbCard);
         mConnectedHasCapture = devicesParser.hasCaptureDevices(mConnectedUsbCard);
         mConnectedHasMIDI = devicesParser.hasMIDIDevices(mConnectedUsbCard);
 
+        // Playback device file needed/present?
+        if (mConnectedHasPlayback &&
+            !waitForAlsaFile(mConnectedUsbCard, mConnectedUsbDeviceNum, false)) {
+            return;
+        }
+
+        // Capture device file needed/present?
+        if (mConnectedHasCapture &&
+            !waitForAlsaFile(mConnectedUsbCard, mConnectedUsbDeviceNum, true)) {
+            return;
+        }
+
         if (DEBUG_AUDIO) {
             Slog.d(TAG,
                     "usb: hasPlayback:" + mConnectedHasPlayback + " hasCapture:" + mConnectedHasCapture);
diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java
index a292587..f988ac8 100644
--- a/telecomm/java/android/telecomm/Call.java
+++ b/telecomm/java/android/telecomm/Call.java
@@ -18,6 +18,7 @@
 
 import android.app.PendingIntent;
 import android.net.Uri;
+import android.os.Bundle;
 import android.telephony.DisconnectCause;
 
 import java.lang.String;
@@ -91,6 +92,7 @@
         private final GatewayInfo mGatewayInfo;
         private final int mVideoState;
         private final StatusHints mStatusHints;
+        private final Bundle mExtras;
 
         /**
          * @return The handle (e.g., phone number) to which the {@code Call} is currently
@@ -186,6 +188,13 @@
             return mStatusHints;
         }
 
+        /**
+         * @return A bundle extras to pass with the call
+         */
+        public Bundle getExtras() {
+            return mExtras;
+        }
+
         @Override
         public boolean equals(Object o) {
             if (o instanceof Details) {
@@ -203,7 +212,8 @@
                         Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
                         Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
                         Objects.equals(mVideoState, d.mVideoState) &&
-                        Objects.equals(mStatusHints, d.mStatusHints);
+                        Objects.equals(mStatusHints, d.mStatusHints) &&
+                        Objects.equals(mExtras, d.mExtras);
             }
             return false;
         }
@@ -222,7 +232,8 @@
                     Objects.hashCode(mConnectTimeMillis) +
                     Objects.hashCode(mGatewayInfo) +
                     Objects.hashCode(mVideoState) +
-                    Objects.hashCode(mStatusHints);
+                    Objects.hashCode(mStatusHints) +
+                    Objects.hashCode(mExtras);
         }
 
         /** {@hide} */
@@ -238,7 +249,8 @@
                 long connectTimeMillis,
                 GatewayInfo gatewayInfo,
                 int videoState,
-                StatusHints statusHints) {
+                StatusHints statusHints,
+                Bundle extras) {
             mHandle = handle;
             mHandlePresentation = handlePresentation;
             mCallerDisplayName = callerDisplayName;
@@ -251,6 +263,7 @@
             mGatewayInfo = gatewayInfo;
             mVideoState = videoState;
             mStatusHints = statusHints;
+            mExtras = extras;
         }
     }
 
@@ -607,7 +620,8 @@
                 parcelableCall.getConnectTimeMillis(),
                 parcelableCall.getGatewayInfo(),
                 parcelableCall.getVideoState(),
-                parcelableCall.getStatusHints());
+                parcelableCall.getStatusHints(),
+                parcelableCall.getExtras());
         boolean detailsChanged = !Objects.equals(mDetails, details);
         if (detailsChanged) {
             mDetails = details;
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index d5e4f1b..44aacfc 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -539,7 +539,10 @@
                         connection.getCallerDisplayNamePresentation(),
                         connection.getVideoProvider() == null ?
                                 null : connection.getVideoProvider().getInterface(),
-                        connection.getVideoState()));
+                        connection.getVideoState(),
+                        connection.isRequestingRingback(),
+                        connection.getAudioModeIsVoip(),
+                        connection.getStatusHints()));
     }
 
     private void abort(String callId) {
diff --git a/telecomm/java/android/telecomm/ParcelableCall.java b/telecomm/java/android/telecomm/ParcelableCall.java
index 2a9a63a..8098b94 100644
--- a/telecomm/java/android/telecomm/ParcelableCall.java
+++ b/telecomm/java/android/telecomm/ParcelableCall.java
@@ -17,6 +17,7 @@
 package android.telecomm;
 
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
@@ -53,6 +54,7 @@
     private final StatusHints mStatusHints;
     private final int mVideoState;
     private final List<String> mConferenceableCallIds;
+    private final Bundle mExtras;
 
     public ParcelableCall(
             String id,
@@ -73,7 +75,8 @@
             List<String> childCallIds,
             StatusHints statusHints,
             int videoState,
-            List<String> conferenceableCallIds) {
+            List<String> conferenceableCallIds,
+            Bundle extras) {
         mId = id;
         mState = state;
         mDisconnectCauseCode = disconnectCauseCode;
@@ -93,6 +96,7 @@
         mStatusHints = statusHints;
         mVideoState = videoState;
         mConferenceableCallIds = Collections.unmodifiableList(conferenceableCallIds);
+        mExtras = extras;
     }
 
     /** The unique ID of the call. */
@@ -220,6 +224,15 @@
         return mVideoState;
     }
 
+    /**
+     * Any extras to pass with the call
+     *
+     * @return a bundle of extras
+     */
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
     /** Responsible for creating ParcelableCall objects for deserialized Parcels. */
     public static final Parcelable.Creator<ParcelableCall> CREATOR =
             new Parcelable.Creator<ParcelableCall> () {
@@ -249,11 +262,12 @@
             int videoState = source.readInt();
             List<String> conferenceableCallIds = new ArrayList<>();
             source.readList(conferenceableCallIds, classLoader);
+            Bundle extras = source.readParcelable(classLoader);
             return new ParcelableCall(id, state, disconnectCauseCode, disconnectCauseMsg,
                     cannedSmsResponses, capabilities, connectTimeMillis, handle, handlePresentation,
                     callerDisplayName, callerDisplayNamePresentation, gatewayInfo,
                     accountHandle, videoCallProvider, parentCallId, childCallIds, statusHints,
-                    videoState, conferenceableCallIds);
+                    videoState, conferenceableCallIds, extras);
         }
 
         @Override
@@ -291,6 +305,7 @@
         destination.writeParcelable(mStatusHints, 0);
         destination.writeInt(mVideoState);
         destination.writeList(mConferenceableCallIds);
+        destination.writeParcelable(mExtras, 0);
     }
 
     @Override
diff --git a/telecomm/java/android/telecomm/ParcelableConnection.java b/telecomm/java/android/telecomm/ParcelableConnection.java
index 78dd64a..7a87b87 100644
--- a/telecomm/java/android/telecomm/ParcelableConnection.java
+++ b/telecomm/java/android/telecomm/ParcelableConnection.java
@@ -38,6 +38,9 @@
     private int mCallerDisplayNamePresentation;
     private IVideoProvider mVideoProvider;
     private int mVideoState;
+    private boolean mRequestingRingback;
+    private boolean mAudioModeIsVoip;
+    private StatusHints mStatusHints;
 
     /** @hide */
     public ParcelableConnection(
@@ -49,7 +52,10 @@
             String callerDisplayName,
             int callerDisplayNamePresentation,
             IVideoProvider videoProvider,
-            int videoState) {
+            int videoState,
+            boolean requestingRingback,
+            boolean audioModeIsVoip,
+            StatusHints statusHints) {
         mPhoneAccount = phoneAccount;
         mState = state;
         mCapabilities = capabilities;
@@ -59,6 +65,9 @@
         mCallerDisplayNamePresentation = callerDisplayNamePresentation;
         mVideoProvider = videoProvider;
         mVideoState = videoState;
+        mRequestingRingback = requestingRingback;
+        mAudioModeIsVoip = audioModeIsVoip;
+        mStatusHints = statusHints;
     }
 
     public PhoneAccountHandle getPhoneAccount() {
@@ -98,6 +107,18 @@
         return mVideoState;
     }
 
+    public boolean isRequestingRingback() {
+        return mRequestingRingback;
+    }
+
+    public boolean getAudioModeIsVoip() {
+        return mAudioModeIsVoip;
+    }
+
+    public final StatusHints getStatusHints() {
+        return mStatusHints;
+    }
+
     @Override
     public String toString() {
         return new StringBuilder()
@@ -126,6 +147,9 @@
             IVideoProvider videoCallProvider =
                     IVideoProvider.Stub.asInterface(source.readStrongBinder());
             int videoState = source.readInt();
+            boolean requestingRingback = source.readByte() == 1;
+            boolean audioModeIsVoip = source.readByte() == 1;
+            StatusHints statusHints = source.readParcelable(classLoader);
 
             return new ParcelableConnection(
                     phoneAccount,
@@ -136,7 +160,10 @@
                     callerDisplayName,
                     callerDisplayNamePresentation,
                     videoCallProvider,
-                    videoState);
+                    videoState,
+                    requestingRingback,
+                    audioModeIsVoip,
+                    statusHints);
         }
 
         @Override
@@ -164,5 +191,8 @@
         destination.writeStrongBinder(
                 mVideoProvider != null ? mVideoProvider.asBinder() : null);
         destination.writeInt(mVideoState);
+        destination.writeByte((byte) (mRequestingRingback ? 1 : 0));
+        destination.writeByte((byte) (mAudioModeIsVoip ? 1 : 0));
+        destination.writeParcelable(mStatusHints, 0);
     }
 }
diff --git a/telecomm/java/android/telecomm/PhoneAccount.java b/telecomm/java/android/telecomm/PhoneAccount.java
index e3e942b..411f48c 100644
--- a/telecomm/java/android/telecomm/PhoneAccount.java
+++ b/telecomm/java/android/telecomm/PhoneAccount.java
@@ -254,7 +254,7 @@
             return null;
         }
         try {
-            return packageContext.getResources().getDrawable(resId);
+            return packageContext.getDrawable(resId);
         } catch (NotFoundException|MissingResourceException e) {
             Log.e(this, e, "Cannot find icon %d in package %s",
                     resId, mAccountHandle.getComponentName().getPackageName());
diff --git a/telecomm/java/android/telecomm/RemoteConnection.java b/telecomm/java/android/telecomm/RemoteConnection.java
index 13b0834..d3972d31 100644
--- a/telecomm/java/android/telecomm/RemoteConnection.java
+++ b/telecomm/java/android/telecomm/RemoteConnection.java
@@ -24,9 +24,11 @@
 import android.telephony.DisconnectCause;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * A connection provided to a {@link ConnectionService} by another {@code ConnectionService}
@@ -182,7 +184,8 @@
 
     private IConnectionService mConnectionService;
     private final String mConnectionId;
-    private final Set<Listener> mListeners = new HashSet<>();
+    private final Set<Listener> mListeners = Collections.newSetFromMap(
+            new ConcurrentHashMap<Listener, Boolean>(2));
     private final Set<RemoteConnection> mConferenceableConnections = new HashSet<>();
 
     private int mState = Connection.STATE_NEW;
diff --git a/telecomm/java/android/telecomm/StatusHints.java b/telecomm/java/android/telecomm/StatusHints.java
index 0cf1295..f7c4f2f 100644
--- a/telecomm/java/android/telecomm/StatusHints.java
+++ b/telecomm/java/android/telecomm/StatusHints.java
@@ -121,7 +121,7 @@
             return null;
         }
         try {
-            return packageContext.getResources().getDrawable(resId);
+            return packageContext.getDrawable(resId);
         } catch (MissingResourceException e) {
             Log.e(this, e, "Cannot find icon %d in package %s",
                     resId, mComponentName.getPackageName());
diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java
index 6d14fa8..5192b0f 100644
--- a/telecomm/java/android/telecomm/TelecommManager.java
+++ b/telecomm/java/android/telecomm/TelecommManager.java
@@ -101,6 +101,17 @@
             "android.intent.extra.INCOMING_CALL_EXTRAS";
 
     /**
+     * Optional extra for {@link android.content.Intent#ACTION_CALL} and
+     * {@link android.content.Intent#ACTION_DIAL} {@code Intent} containing a {@link Bundle}
+     * which contains metadata about the call. This {@link Bundle} will be saved into
+     * {@code Call.Details}.
+     *
+     * @hide
+     */
+    public static final String EXTRA_OUTGOING_CALL_EXTRAS =
+            "android.intent.extra.OUTGOING_CALL_EXTRAS";
+
+    /**
      * Optional extra for {@link android.telephony.TelephonyManager#ACTION_PHONE_STATE_CHANGED}
      * containing the disconnect code.
      */
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 15298599..a8a9057 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -287,6 +287,7 @@
     int RIL_REQUEST_ALLOW_DATA = 123;
     int RIL_REQUEST_GET_HARDWARE_CONFIG = 124;
     int RIL_REQUEST_SIM_AUTHENTICATION = 125;
+    int RIL_REQUEST_SET_DATA_PROFILE = 128;
 
     int RIL_UNSOL_RESPONSE_BASE = 1000;
     int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000;
diff --git a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java
index c0f3a7f..526ea5d 100644
--- a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java
+++ b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 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.
diff --git a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java
index 3fc468d..2fc77dc 100644
--- a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java
+++ b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 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.
diff --git a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/MainActivity.java b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/MainActivity.java
index ed91aad..4c28234 100644
--- a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/MainActivity.java
+++ b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/MainActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 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.
diff --git a/tests/OneMedia/AndroidManifest.xml b/tests/OneMedia/AndroidManifest.xml
index 9d78ca5..95072a4 100644
--- a/tests/OneMedia/AndroidManifest.xml
+++ b/tests/OneMedia/AndroidManifest.xml
@@ -23,7 +23,7 @@
         </activity>
         <service
             android:name="com.android.onemedia.OnePlayerService"
-            android:exported="false"
+            android:exported="true"
             android:process="com.android.onemedia.service" />
         <service
             android:name=".provider.OneMediaRouteProvider"
diff --git a/tests/OneMedia/res/drawable/ic_fast_forward.xml b/tests/OneMedia/res/drawable/ic_fast_forward.xml
new file mode 100644
index 0000000..8daf07d
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_fast_forward.xml
@@ -0,0 +1,25 @@
+<!--
+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="36dp"
+    android:height="36dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24" >
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M4.0,18.0l8.5,-6.0L4.0,6.0L4.0,18.0zM13.0,6.0l0.0,12.0l8.5,-6.0L13.0,6.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/drawable/ic_fast_rewind.xml b/tests/OneMedia/res/drawable/ic_fast_rewind.xml
new file mode 100644
index 0000000..4ed1f54
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_fast_rewind.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="36dp"
+    android:height="36dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24" >
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M11.0,18.0L11.0,6.0l-8.5,6.0L11.0,18.0zM11.5,12.0l8.5,6.0L20.0,6.0L11.5,12.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/drawable/ic_pause.xml b/tests/OneMedia/res/drawable/ic_pause.xml
new file mode 100644
index 0000000..15d0756
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_pause.xml
@@ -0,0 +1,25 @@
+<!--
+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="36dp"
+    android:height="36dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24" >
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M6.0,19.0l4.0,0.0L10.0,5.0L6.0,5.0L6.0,19.0zM14.0,5.0l0.0,14.0l4.0,0.0L18.0,5.0L14.0,5.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/drawable/ic_play_arrow.xml b/tests/OneMedia/res/drawable/ic_play_arrow.xml
new file mode 100644
index 0000000..49d766d
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_play_arrow.xml
@@ -0,0 +1,25 @@
+<!--
+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="36dp"
+    android:height="36dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24" >
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M8.0,5.0l0.0,14.0 11.0,-7.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/drawable/ic_skip_next.xml b/tests/OneMedia/res/drawable/ic_skip_next.xml
new file mode 100644
index 0000000..8a6ceca
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_skip_next.xml
@@ -0,0 +1,25 @@
+<!--
+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="36dp"
+    android:height="36dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24" >
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M6.0,18.0l8.5,-6.0L6.0,6.0L6.0,18.0zM16.0,6.0l0.0,12.0l2.0,0.0L18.0,6.0L16.0,6.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/drawable/ic_skip_previous.xml b/tests/OneMedia/res/drawable/ic_skip_previous.xml
new file mode 100644
index 0000000..c5d07db
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_skip_previous.xml
@@ -0,0 +1,28 @@
+<!--
+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="36dp"
+    android:height="36dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24" >
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M6.0,6.0l2.0,0.0l0.0,12.0l-2.0,0.0z"/>
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M9.5,12.0l8.5,6.0 0.0,-12.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/drawable/ic_stop.xml b/tests/OneMedia/res/drawable/ic_stop.xml
new file mode 100644
index 0000000..6043fdb6
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_stop.xml
@@ -0,0 +1,25 @@
+<!--
+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="36dp"
+    android:height="36dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24" >
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M6.0,6.0l12.0,0.0l0.0,12.0l-12.0,0.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/layout/activity_one_player.xml b/tests/OneMedia/res/layout/activity_one_player.xml
index 516562f..ce2d641 100644
--- a/tests/OneMedia/res/layout/activity_one_player.xml
+++ b/tests/OneMedia/res/layout/activity_one_player.xml
@@ -33,6 +33,19 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="@string/has_video" />
+    <ImageView
+            android:id="@+id/art"
+            android:layout_width="match_parent"
+            android:layout_height="96dp"
+            android:scaleType="centerCrop"
+            android:visibility="gone"
+            />
+    <Button
+            android:id="@+id/art_picker"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/art_picker"
+            />
     <LinearLayout
             android:id="@+id/controls"
             android:layout_width="match_parent"
diff --git a/tests/OneMedia/res/values/strings.xml b/tests/OneMedia/res/values/strings.xml
index 3735c8d..86657fd 100644
--- a/tests/OneMedia/res/values/strings.xml
+++ b/tests/OneMedia/res/values/strings.xml
@@ -12,5 +12,5 @@
     <string name="media_next_hint">Next content</string>
     <string name="has_video">Is video</string>
     <string name="has_duration">Has duration</string>
-
+    <string name="art_picker">Choose artwork</string>
 </resources>
diff --git a/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl b/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl
index d4df4c5..f53eac0 100644
--- a/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl
+++ b/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl
@@ -15,6 +15,7 @@
 
 package com.android.onemedia;
 
+import android.graphics.Bitmap;
 import android.media.session.MediaSession;
 import android.os.Bundle;
 
@@ -26,4 +27,5 @@
     void registerCallback(in IPlayerCallback cb);
     void unregisterCallback(in IPlayerCallback cb);
     void sendRequest(String action, in Bundle params, in IRequestCallback cb);
+    void setIcon(in Bitmap icon);
 }
diff --git a/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java b/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java
new file mode 100644
index 0000000..a5bcda5
--- /dev/null
+++ b/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java
@@ -0,0 +1,234 @@
+package com.android.onemedia;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Bitmap;
+import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.onemedia.playback.RequestUtils;
+
+/**
+ * Keeps track of a notification and updates it automatically for a given
+ * MediaSession.
+ */
+public class NotificationHelper extends BroadcastReceiver {
+    private static final String TAG = "NotificationHelper";
+
+    private static final int NOTIFICATION_ID = 433; // John Cage, 1952
+
+    private final Service mService;
+    private final MediaSession mSession;
+    private final MediaController mController;
+    private final MediaController.TransportControls mTransportControls;
+    private final SparseArray<PendingIntent> mIntents = new SparseArray<PendingIntent>();
+
+    private PlaybackState mPlaybackState;
+    private MediaMetadata mMetadata;
+
+    private boolean mStarted = false;
+
+    public NotificationHelper(Service service, MediaSession session) {
+        mService = service;
+        mSession = session;
+        mController = session.getController();
+        mTransportControls = mController.getTransportControls();
+        String pkg = mService.getPackageName();
+
+        mIntents.put(R.drawable.ic_pause, PendingIntent.getBroadcast(mService, 100, new Intent(
+                com.android.onemedia.playback.RequestUtils.ACTION_PAUSE).setPackage(pkg),
+                PendingIntent.FLAG_CANCEL_CURRENT));
+        mIntents.put(R.drawable.ic_play_arrow, PendingIntent.getBroadcast(mService, 100,
+                new Intent(com.android.onemedia.playback.RequestUtils.ACTION_PLAY).setPackage(pkg),
+                PendingIntent.FLAG_CANCEL_CURRENT));
+        mIntents.put(R.drawable.ic_skip_previous, PendingIntent.getBroadcast(mService, 100,
+                new Intent(com.android.onemedia.playback.RequestUtils.ACTION_PREV).setPackage(pkg),
+                PendingIntent.FLAG_CANCEL_CURRENT));
+        mIntents.put(R.drawable.ic_skip_next, PendingIntent.getBroadcast(mService, 100,
+                new Intent(com.android.onemedia.playback.RequestUtils.ACTION_NEXT).setPackage(pkg),
+                PendingIntent.FLAG_CANCEL_CURRENT));
+        mIntents.put(R.drawable.ic_fast_rewind, PendingIntent.getBroadcast(mService, 100,
+                new Intent(com.android.onemedia.playback.RequestUtils.ACTION_REW).setPackage(pkg),
+                PendingIntent.FLAG_CANCEL_CURRENT));
+        mIntents.put(R.drawable.ic_fast_forward, PendingIntent.getBroadcast(mService, 100,
+                new Intent(com.android.onemedia.playback.RequestUtils.ACTION_FFWD).setPackage(pkg),
+                PendingIntent.FLAG_CANCEL_CURRENT));
+    }
+
+    /**
+     * Posts the notification and starts tracking the session to keep it
+     * updated. The notification will automatically be removed if the session is
+     * destroyed before {@link #onStop} is called.
+     */
+    public void onStart() {
+        mController.addCallback(mCb);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(RequestUtils.ACTION_FFWD);
+        filter.addAction(RequestUtils.ACTION_NEXT);
+        filter.addAction(RequestUtils.ACTION_PAUSE);
+        filter.addAction(RequestUtils.ACTION_PLAY);
+        filter.addAction(RequestUtils.ACTION_PREV);
+        filter.addAction(RequestUtils.ACTION_REW);
+        mService.registerReceiver(this, filter);
+
+        mMetadata = mController.getMetadata();
+        mPlaybackState = mController.getPlaybackState();
+
+        mStarted = true;
+        // The notification must be updated after setting started to true
+        updateNotification();
+    }
+
+    /**
+     * Removes the notification and stops tracking the session. If the session
+     * was destroyed this has no effect.
+     */
+    public void onStop() {
+        mStarted = false;
+        mController.removeCallback(mCb);
+        mService.unregisterReceiver(this);
+        updateNotification();
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        Log.d(TAG, "Received intent with action " + action);
+        if (RequestUtils.ACTION_PAUSE.equals(action)) {
+            mTransportControls.pause();
+        } else if (RequestUtils.ACTION_PLAY.equals(action)) {
+            mTransportControls.play();
+        } else if (RequestUtils.ACTION_NEXT.equals(action)) {
+            mTransportControls.skipToNext();
+        } else if (RequestUtils.ACTION_PREV.equals(action)) {
+            mTransportControls.skipToPrevious();
+        } else if (RequestUtils.ACTION_REW.equals(action)) {
+            mTransportControls.rewind();
+        } else if (RequestUtils.ACTION_FFWD.equals(action)) {
+            mTransportControls.fastForward();
+        }
+
+    }
+
+    private final MediaController.Callback mCb = new MediaController.Callback() {
+        @Override
+        public void onPlaybackStateChanged(PlaybackState state) {
+            mPlaybackState = state;
+            Log.d(TAG, "Received new playback state" + state);
+            updateNotification();
+        }
+
+        @Override
+        public void onMetadataChanged(MediaMetadata metadata) {
+            mMetadata = metadata;
+            Log.d(TAG, "Received new metadata " + metadata);
+            updateNotification();
+        }
+    };
+
+    NotificationManager mNoMan = null;
+
+    private void updateNotification() {
+        if (mNoMan == null) {
+            mNoMan = (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
+        }
+        if (mPlaybackState == null) {
+            mNoMan.cancel(NOTIFICATION_ID);
+            return;
+        }
+        if (!mStarted) {
+            mNoMan.cancel(NOTIFICATION_ID);
+            return;
+        }
+
+        String status;
+        final int state = mPlaybackState.getState();
+        switch (state) {
+            case PlaybackState.STATE_PLAYING:
+                status = "PLAYING: ";
+                break;
+            case PlaybackState.STATE_PAUSED:
+                status = "PAUSED: ";
+                break;
+            case PlaybackState.STATE_STOPPED:
+                status = "STOPPED: ";
+                break;
+            case PlaybackState.STATE_ERROR:
+                status = "ERROR: ";
+                break;
+            case PlaybackState.STATE_BUFFERING:
+                status = "BUFFERING: ";
+                break;
+            case PlaybackState.STATE_NONE:
+            default:
+                status = "";
+                break;
+        }
+        CharSequence title, text;
+        Bitmap art;
+        if (mMetadata == null) {
+            title = status;
+            text = "Empty metadata!";
+            art = null;
+        } else {
+            MediaMetadata.Description description = mMetadata.getDescription();
+            title = description.getTitle();
+            text = description.getSubtitle();
+            art = description.getIcon();
+        }
+
+        String playPauseLabel = "";
+        int playPauseIcon;
+        if (state == PlaybackState.STATE_PLAYING) {
+            playPauseLabel = "Pause";
+            playPauseIcon = R.drawable.ic_pause;
+        } else {
+            playPauseLabel = "Play";
+            playPauseIcon = R.drawable.ic_play_arrow;
+        }
+
+        final long pos = mPlaybackState.getPosition();
+        final long end = mMetadata == null ? 0 : mMetadata
+                .getLong(MediaMetadata.METADATA_KEY_DURATION);
+        Notification notification = new Notification.Builder(mService)
+                .setSmallIcon(android.R.drawable.stat_notify_chat)
+                .setContentTitle(title)
+                .setContentText(text)
+                .setShowWhen(false)
+                .setContentInfo(DateUtils.formatElapsedTime(pos))
+                .setProgress((int) end, (int) pos, false)
+                .setLargeIcon(art)
+                .addAction(R.drawable.ic_skip_previous, "Previous",
+                        mIntents.get(R.drawable.ic_skip_previous))
+                .addAction(R.drawable.ic_fast_rewind, "Rewind",
+                        mIntents.get(R.drawable.ic_fast_rewind))
+                .addAction(playPauseIcon, playPauseLabel,
+                        mIntents.get(playPauseIcon))
+                .addAction(R.drawable.ic_fast_forward, "Fast Forward",
+                        mIntents.get(R.drawable.ic_fast_forward))
+                .addAction(R.drawable.ic_skip_next, "Next",
+                        mIntents.get(R.drawable.ic_skip_next))
+                .setStyle(new Notification.MediaStyle()
+                        .setShowActionsInCompactView(2)
+                        .setMediaSession(mSession.getSessionToken()))
+                .setColor(0xFFDB4437)
+                .build();
+
+        mService.startForeground(NOTIFICATION_ID, notification);
+    }
+
+}
diff --git a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
index 894377b..2ff3e20 100644
--- a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
+++ b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
@@ -17,20 +17,35 @@
 
 
 import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.media.MediaMetadata;
 import android.media.session.PlaybackState;
+import android.net.Uri;
 import android.os.Bundle;
+import android.provider.MediaStore;
+import android.text.format.DateUtils;
 import android.util.Log;
 import android.view.Menu;
 import android.view.View;
 import android.widget.Button;
 import android.widget.CheckBox;
 import android.widget.EditText;
+import android.widget.ImageView;
 import android.widget.TextView;
 
+import java.io.IOException;
+
 public class OnePlayerActivity extends Activity {
     private static final String TAG = "OnePlayerActivity";
 
+    private static final int READ_REQUEST_CODE = 42;
+
     protected PlayerController mPlayer;
 
     private Button mStartButton;
@@ -41,8 +56,10 @@
     private EditText mContentText;
     private EditText mNextContentText;
     private CheckBox mHasVideo;
+    private ImageView mArtView;
 
-    private int mPlaybackState;
+    private PlaybackState mPlaybackState;
+    private Bitmap mAlbumArtBitmap;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -58,6 +75,10 @@
         mContentText = (EditText) findViewById(R.id.content);
         mNextContentText = (EditText) findViewById(R.id.next_content);
         mHasVideo = (CheckBox) findViewById(R.id.has_video);
+        mArtView = (ImageView) findViewById(R.id.art);
+
+        final Button artPicker = (Button) findViewById(R.id.art_picker);
+        artPicker.setOnClickListener(mButtonListener);
 
         mStartButton.setOnClickListener(mButtonListener);
         mPlayButton.setOnClickListener(mButtonListener);
@@ -86,6 +107,31 @@
         super.onPause();
     }
 
+    @Override
+    public void onActivityResult(int requestCode, int resultCode,
+            Intent resultData) {
+        if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
+            Uri uri = null;
+            if (resultData != null) {
+                uri = resultData.getData();
+                Log.i(TAG, "Uri: " + uri.toString());
+                mAlbumArtBitmap = null;
+                try {
+                    mAlbumArtBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
+                } catch (IOException e) {
+                    Log.v(TAG, "Couldn't load album art", e);
+                }
+                mArtView.setImageBitmap(mAlbumArtBitmap);
+                if (mAlbumArtBitmap != null) {
+                    mArtView.setVisibility(View.VISIBLE);
+                } else {
+                    mArtView.setVisibility(View.GONE);
+                }
+                mPlayer.setArt(mAlbumArtBitmap);
+            }
+        }
+    }
+
     private void setControlsEnabled(boolean enabled) {
         mStartButton.setEnabled(enabled);
         mPlayButton.setEnabled(enabled);
@@ -94,36 +140,46 @@
     private View.OnClickListener mButtonListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
+            final int state = mPlaybackState.getState();
             switch (v.getId()) {
                 case R.id.play_button:
-                    Log.d(TAG, "Play button pressed, in state " + mPlaybackState);
-                    if (mPlaybackState == PlaybackState.STATE_PAUSED
-                            || mPlaybackState == PlaybackState.STATE_STOPPED) {
+                    Log.d(TAG, "Play button pressed, in state " + state);
+                    if (state == PlaybackState.STATE_PAUSED
+                            || state == PlaybackState.STATE_STOPPED) {
                         mPlayer.play();
-                    } else if (mPlaybackState == PlaybackState.STATE_PLAYING) {
+                    } else if (state == PlaybackState.STATE_PLAYING) {
                         mPlayer.pause();
                     }
                     break;
                 case R.id.start_button:
-                    Log.d(TAG, "Start button pressed, in state " + mPlaybackState);
+                    Log.d(TAG, "Start button pressed, in state " + state);
                     mPlayer.setContent(mContentText.getText().toString());
                     break;
                 case R.id.route_button:
                     mPlayer.showRoutePicker();
                     break;
+                case R.id.art_picker:
+                    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+                    intent.addCategory(Intent.CATEGORY_OPENABLE);
+                    intent.setType("image/*");
+
+                    startActivityForResult(intent, READ_REQUEST_CODE);
+                    break;
             }
 
         }
     };
 
     private PlayerController.Listener mListener = new PlayerController.Listener() {
+        public MediaMetadata mMetadata;
+
         @Override
         public void onPlaybackStateChange(PlaybackState state) {
-            mPlaybackState = state.getState();
+            mPlaybackState = state;
             boolean enablePlay = false;
             boolean enableControls = true;
             StringBuilder statusBuilder = new StringBuilder();
-            switch (mPlaybackState) {
+            switch (mPlaybackState.getState()) {
                 case PlaybackState.STATE_PLAYING:
                     statusBuilder.append("playing");
                     mPlayButton.setText("Pause");
@@ -172,7 +228,7 @@
 
         @Override
         public void onMetadataChange(MediaMetadata metadata) {
-            Log.d(TAG, "Metadata update! Title: " + metadata);
+            mMetadata = metadata;
         }
     };
 }
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerController.java b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
index c0799fc..c8d72ca 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerController.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
@@ -30,6 +30,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.graphics.Bitmap;
 import android.util.Log;
 
 import com.android.onemedia.playback.RequestUtils;
@@ -52,6 +53,7 @@
     private Handler mHandler = new Handler();
 
     private boolean mResumed;
+    private Bitmap mArt;
 
     public PlayerController(Activity context, Intent serviceIntent) {
         mContext = context;
@@ -89,6 +91,16 @@
         unbindFromService();
     }
 
+    public void setArt(Bitmap art) {
+        mArt = art;
+        if (mBinder != null) {
+            try {
+                mBinder.setIcon(art);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
     public void play() {
         if (mTransportControls != null) {
             mTransportControls.play();
@@ -125,6 +137,16 @@
         // TODO
     }
 
+    public MediaSession.Token getSessionToken() {
+        if (mBinder != null) {
+            try {
+                return mBinder.getSessionToken();
+            } catch (RemoteException e) {
+            }
+        }
+        return null;
+    }
+
     private void unbindFromService() {
         mContext.unbindService(mServiceConnection);
     }
@@ -165,6 +187,9 @@
             mContext.setMediaController(mController);
             mController.addCallback(mControllerCb, mHandler);
             mTransportControls = mController.getTransportControls();
+            if (mArt != null) {
+                setArt(mArt);
+            }
             Log.d(TAG, "Ready to use PlayerService");
 
             if (mListener != null) {
@@ -194,6 +219,9 @@
                 return;
             }
             Log.d(TAG, "Received metadata change, " + metadata.getDescription());
+            if (mListener != null) {
+                mListener.onMetadataChange(metadata);
+            }
         }
     }
 
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerService.java b/tests/OneMedia/src/com/android/onemedia/PlayerService.java
index 58ee4a1..9967c99 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerService.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerService.java
@@ -17,6 +17,7 @@
 
 import android.app.Service;
 import android.content.Intent;
+import android.graphics.Bitmap;
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
@@ -34,6 +35,7 @@
 
     private PlayerBinder mBinder;
     private PlayerSession mSession;
+    private NotificationHelper mNotifyHelper;
     private Intent mIntent;
     private boolean mStarted = false;
 
@@ -47,6 +49,7 @@
             mSession = onCreatePlayerController();
             mSession.createSession();
             mSession.setListener(mPlayerListener);
+            mNotifyHelper = new NotificationHelper(this, mSession.mSession);
         }
     }
 
@@ -75,6 +78,7 @@
         if (!mStarted) {
             Log.d(TAG, "Starting self");
             startService(onCreateServiceIntent());
+            mNotifyHelper.onStart();
             mStarted = true;
         }
     }
@@ -82,6 +86,7 @@
     public void onPlaybackEnded() {
         if (mStarted) {
             Log.d(TAG, "Stopping self");
+            mNotifyHelper.onStop();
             stopSelf();
             mStarted = false;
         }
@@ -150,8 +155,17 @@
 
         @Override
         public MediaSession.Token getSessionToken() throws RemoteException {
+            if (mSession == null) {
+                Log.e(TAG, "Error in PlayerService: mSession=null in getSessionToken()");
+                return null;
+            }
             return mSession.getSessionToken();
         }
+
+        @Override
+        public void setIcon(Bitmap icon) {
+            mSession.setIcon(icon);
+        }
     }
 
 }
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
index 890d68d..9afcf24 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
@@ -17,6 +17,8 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Bitmap;
+import android.media.MediaMetadata;
 import android.media.routing.MediaRouteSelector;
 import android.media.routing.MediaRouter;
 import android.media.routing.MediaRouter.ConnectionRequest;
@@ -50,6 +52,7 @@
     protected Renderer mRenderer;
     protected MediaSession.Callback mCallback;
     protected Renderer.Listener mRenderListener;
+    protected MediaMetadata.Builder mMetadataBuilder;
 
     protected PlaybackState mPlaybackState;
     protected Listener mListener;
@@ -66,6 +69,8 @@
         mPlaybackState = psBob.build();
 
         mRenderer.registerListener(mRenderListener);
+
+        initMetadata();
     }
 
     public void createSession() {
@@ -92,6 +97,7 @@
                 | MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
         mSession.setMediaRouter(mRouter);
         mSession.setActive(true);
+        updateMetadata();
     }
 
     public void onDestroy() {
@@ -130,6 +136,19 @@
         mRenderer.setNextContent(request);
     }
 
+    public void setIcon(Bitmap icon) {
+        mMetadataBuilder.putBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON, icon);
+        updateMetadata();
+    }
+
+    private void updateMetadata() {
+        // This is a mild abuse of metadata and shouldn't be duplicated in real
+        // code
+        if (mSession != null && mSession.isActive()) {
+            mSession.setMetadata(mMetadataBuilder.build());
+        }
+    }
+
     private void updateState(int newState) {
         float rate = newState == PlaybackState.STATE_PLAYING ? 1 : 0;
         long position = mRenderer == null ? -1 : mRenderer.getSeekPosition();
@@ -140,6 +159,14 @@
         mSession.setPlaybackState(mPlaybackState);
     }
 
+    private void initMetadata() {
+        mMetadataBuilder = new MediaMetadata.Builder();
+        mMetadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE,
+                "OneMedia display title");
+        mMetadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE,
+                "OneMedia display subtitle");
+    }
+
     public interface Listener {
         public void onPlayStateChanged(PlaybackState state);
     }
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java b/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java
index 3778c5f..1688395 100644
--- a/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java
+++ b/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java
@@ -26,6 +26,12 @@
 public class RequestUtils {
     public static final String ACTION_SET_CONTENT = "set_content";
     public static final String ACTION_SET_NEXT_CONTENT = "set_next_content";
+    public static final String ACTION_PAUSE = "com.android.onemedia.pause";
+    public static final String ACTION_PLAY = "com.android.onemedia.play";
+    public static final String ACTION_REW = "com.android.onemedia.rew";
+    public static final String ACTION_FFWD = "com.android.onemedia.ffwd";
+    public static final String ACTION_PREV = "com.android.onemedia.prev";
+    public static final String ACTION_NEXT = "com.android.onemedia.next";
 
     public static final String EXTRA_KEY_SOURCE = "source";
     public static final String EXTRA_KEY_METADATA = "metadata";
diff --git a/tools/aapt/ApkBuilder.cpp b/tools/aapt/ApkBuilder.cpp
index 12f6040..01e02e2 100644
--- a/tools/aapt/ApkBuilder.cpp
+++ b/tools/aapt/ApkBuilder.cpp
@@ -85,11 +85,24 @@
         if (mName.size() > 0) {
             mName.append(",");
             mDirName.append("_");
+            mPackageSafeName.append(".");
         }
 
         String8 configStr = iter->toString();
+        String8 packageConfigStr(configStr);
+        size_t len = packageConfigStr.length();
+        if (len > 0) {
+            char* buf = packageConfigStr.lockBuffer(len);
+            for (char* end = buf + len; buf < end; ++buf) {
+                if (*buf == '-') {
+                    *buf = '_';
+                }
+            }
+            packageConfigStr.unlockBuffer(len);
+        }
         mName.append(configStr);
         mDirName.append(configStr);
+        mPackageSafeName.append(packageConfigStr);
     }
 }
 
diff --git a/tools/aapt/ApkBuilder.h b/tools/aapt/ApkBuilder.h
index db23c84..0d7f06b 100644
--- a/tools/aapt/ApkBuilder.h
+++ b/tools/aapt/ApkBuilder.h
@@ -95,6 +95,10 @@
         return mDirName;
     }
 
+    const android::String8& getPackageSafeName() const {
+        return mPackageSafeName;
+    }
+
     bool isBase() const {
         return mIsBase;
     }
@@ -111,6 +115,7 @@
     const bool mIsBase;
     String8 mName;
     String8 mDirName;
+    String8 mPackageSafeName;
     std::set<OutputEntry> mFiles;
 };
 
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index cf3dd0a..137c85c 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -485,9 +485,13 @@
     find_max_opacity(image->rows, innerStartX, innerStartY, innerMidX, innerMidY, 1, 1,
             &diagonalInset);
 
-    // Determine source radius based upon inset
-    // radius = 1 / (sqrt(2) - 1) * inset
-    image->outlineRadius = 2.4142f * diagonalInset;
+    /* Determine source radius based upon inset:
+     *     sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
+     *     sqrt(2) * r = sqrt(2) * i + r
+     *     (sqrt(2) - 1) * r = sqrt(2) * i
+     *     r = sqrt(2) / (sqrt(2) - 1) * i
+     */
+    image->outlineRadius = 3.4142f * diagonalInset;
 
     NOISY(printf("outline insets %d %d %d %d, rad %f, alpha %x\n",
             image->outlineInsetsLeft,
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 2401b3a..010d59b 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -937,8 +937,8 @@
     }
 
     // Add the 'split' attribute which describes the configurations included.
-    String8 splitName("config_");
-    splitName.append(split->getDirectorySafeName());
+    String8 splitName("config.");
+    splitName.append(split->getPackageSafeName());
     manifest->addAttribute(String16(), String16("split"), String16(splitName));
 
     // Build an empty <application> tag (required).