Merge "AAPT2: Process <java-symbols> and private symbol package"
diff --git a/api/current.txt b/api/current.txt
index 4a1a458..55b5d52 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -30532,6 +30532,8 @@
     field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
     field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
     field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
+    field public static final java.lang.String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING = "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
+    field public static final java.lang.String EXTRA_CALL_SUBJECT_MAX_LENGTH = "android.telecom.extra.CALL_SUBJECT_MAX_LENGTH";
     field public static final int NO_HIGHLIGHT_COLOR = 0; // 0x0
     field public static final int NO_RESOURCE_ID = -1; // 0xffffffff
     field public static final java.lang.String SCHEME_SIP = "sip";
@@ -35330,8 +35332,10 @@
     field public static final int KEYCODE_CLEAR = 28; // 0x1c
     field public static final int KEYCODE_COMMA = 55; // 0x37
     field public static final int KEYCODE_CONTACTS = 207; // 0xcf
+    field public static final int KEYCODE_COPY = 278; // 0x116
     field public static final int KEYCODE_CTRL_LEFT = 113; // 0x71
     field public static final int KEYCODE_CTRL_RIGHT = 114; // 0x72
+    field public static final int KEYCODE_CUT = 277; // 0x115
     field public static final int KEYCODE_D = 32; // 0x20
     field public static final int KEYCODE_DEL = 67; // 0x43
     field public static final int KEYCODE_DPAD_CENTER = 23; // 0x17
@@ -35449,6 +35453,7 @@
     field public static final int KEYCODE_PAGE_DOWN = 93; // 0x5d
     field public static final int KEYCODE_PAGE_UP = 92; // 0x5c
     field public static final int KEYCODE_PAIRING = 225; // 0xe1
+    field public static final int KEYCODE_PASTE = 279; // 0x117
     field public static final int KEYCODE_PERIOD = 56; // 0x38
     field public static final int KEYCODE_PICTSYMBOLS = 94; // 0x5e
     field public static final int KEYCODE_PLUS = 81; // 0x51
diff --git a/api/system-current.txt b/api/system-current.txt
index ecea9b0..06cbfa7 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -9536,6 +9536,7 @@
     method public void setAppLabel(java.lang.CharSequence);
     method public void setAppPackageName(java.lang.String);
     method public void setGrantedRuntimePermissions(java.lang.String[]);
+    method public void setInstallFlagsQuick();
     method public void setInstallLocation(int);
     method public void setOriginatingUid(int);
     method public void setOriginatingUri(android.net.Uri);
@@ -32733,6 +32734,8 @@
     field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
     field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
     field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
+    field public static final java.lang.String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING = "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
+    field public static final java.lang.String EXTRA_CALL_SUBJECT_MAX_LENGTH = "android.telecom.extra.CALL_SUBJECT_MAX_LENGTH";
     field public static final int NO_HIGHLIGHT_COLOR = 0; // 0x0
     field public static final int NO_RESOURCE_ID = -1; // 0xffffffff
     field public static final java.lang.String SCHEME_SIP = "sip";
@@ -37626,8 +37629,10 @@
     field public static final int KEYCODE_CLEAR = 28; // 0x1c
     field public static final int KEYCODE_COMMA = 55; // 0x37
     field public static final int KEYCODE_CONTACTS = 207; // 0xcf
+    field public static final int KEYCODE_COPY = 278; // 0x116
     field public static final int KEYCODE_CTRL_LEFT = 113; // 0x71
     field public static final int KEYCODE_CTRL_RIGHT = 114; // 0x72
+    field public static final int KEYCODE_CUT = 277; // 0x115
     field public static final int KEYCODE_D = 32; // 0x20
     field public static final int KEYCODE_DEL = 67; // 0x43
     field public static final int KEYCODE_DPAD_CENTER = 23; // 0x17
@@ -37745,6 +37750,7 @@
     field public static final int KEYCODE_PAGE_DOWN = 93; // 0x5d
     field public static final int KEYCODE_PAGE_UP = 92; // 0x5c
     field public static final int KEYCODE_PAIRING = 225; // 0xe1
+    field public static final int KEYCODE_PASTE = 279; // 0x117
     field public static final int KEYCODE_PERIOD = 56; // 0x38
     field public static final int KEYCODE_PICTSYMBOLS = 94; // 0x5e
     field public static final int KEYCODE_PLUS = 81; // 0x51
diff --git a/cmds/idmap/create.cpp b/cmds/idmap/create.cpp
index 9d60ee1..6b2f460 100644
--- a/cmds/idmap/create.cpp
+++ b/cmds/idmap/create.cpp
@@ -57,7 +57,7 @@
 
     int write_idmap(int fd, const uint32_t *data, size_t size)
     {
-        if (lseek(fd, SEEK_SET, 0) < 0) {
+        if (lseek(fd, 0, SEEK_SET) < 0) {
             return -1;
         }
         size_t bytesLeft = size;
@@ -86,7 +86,7 @@
 
         char buf[N];
         size_t bytesLeft = N;
-        if (lseek(idmap_fd, SEEK_SET, 0) < 0) {
+        if (lseek(idmap_fd, 0, SEEK_SET) < 0) {
             return true;
         }
         for (;;) {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index b44aab7..e038a72 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -465,9 +465,6 @@
     // If set this fragment is being retained across the current config change.
     boolean mRetaining;
 
-    // If set this fragment's loaders are being retained across the current config change.
-    boolean mRetainLoader;
-
     // If set this fragment has menu items to contribute.
     boolean mHasMenu;
 
@@ -2415,7 +2412,7 @@
                 mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
             }
             if (mLoaderManager != null) {
-                if (mRetainLoader) {
+                if (mHost.getRetainLoaders()) {
                     mLoaderManager.doRetain();
                 } else {
                     mLoaderManager.doStop();
diff --git a/core/java/android/app/FragmentController.java b/core/java/android/app/FragmentController.java
index 1b45137..28dadfa 100644
--- a/core/java/android/app/FragmentController.java
+++ b/core/java/android/app/FragmentController.java
@@ -341,7 +341,6 @@
      */
     public void doLoaderStop(boolean retain) {
         mHost.doLoaderStop(retain);
-        mHost.mFragmentManager.setRetainLoader(retain);
     }
 
     /**
diff --git a/core/java/android/app/FragmentHostCallback.java b/core/java/android/app/FragmentHostCallback.java
index 7b01307..13517e6 100644
--- a/core/java/android/app/FragmentHostCallback.java
+++ b/core/java/android/app/FragmentHostCallback.java
@@ -42,9 +42,14 @@
     private final Handler mHandler;
     final int mWindowAnimations;
     final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
+    /** The loader managers for individual fragments [i.e. Fragment#getLoaderManager()] */
     private ArrayMap<String, LoaderManager> mAllLoaderManagers;
+    /** Whether or not fragment loaders should retain their state */
+    private boolean mRetainLoaders;
+    /** The loader manger for the fragment host [i.e. Activity#getLoaderManager()] */
     private LoaderManagerImpl mLoaderManager;
     private boolean mCheckedForLoaderManager;
+    /** Whether or not the fragment host loader manager was started */
     private boolean mLoadersStarted;
 
     public FragmentHostCallback(Context context, Handler handler, int windowAnimations) {
@@ -166,6 +171,10 @@
         return true;
     }
 
+    boolean getRetainLoaders() {
+        return mRetainLoaders;
+    }
+
     Activity getActivity() {
         return mActivity;
     }
@@ -217,6 +226,8 @@
     }
 
     void doLoaderStop(boolean retain) {
+        mRetainLoaders = retain;
+
         if (mLoaderManager == null) {
             return;
         }
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 046c87a..696ccdb 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -869,17 +869,6 @@
         }
     }
 
-    void setRetainLoader(boolean retain) {
-        if (mActive != null) {
-            for (int i=0; i<mActive.size(); i++) {
-                Fragment f = mActive.get(i);
-                if (f != null) {
-                    f.mRetainLoader = retain;
-                }
-            }
-        }
-    }
-
     void moveToState(Fragment f, int newState, int transit, int transitionStyle,
             boolean keepActive) {
         if (DEBUG && false) Log.v(TAG, "moveToState: " + f
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 3283005..d6d395b 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1042,6 +1042,12 @@
         }
 
         /** {@hide} */
+        @SystemApi
+        public void setInstallFlagsQuick() {
+            installFlags |= PackageManager.INSTALL_QUICK;
+        }
+
+        /** {@hide} */
         public void dump(IndentingPrintWriter pw) {
             pw.printPair("mode", mode);
             pw.printHexPair("installFlags", installFlags);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 82cbbbe..aed1a0b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -43,12 +43,10 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.storage.VolumeInfo;
-import android.text.TextUtils;
 import android.util.AndroidException;
 
 import com.android.internal.util.ArrayUtils;
@@ -431,6 +429,14 @@
     public static final int INSTALL_FORCE_PERMISSION_PROMPT = 0x00000400;
 
     /**
+     * Flag parameter for {@link #installPackage} to indicate that this package is
+     * to be installed quickly.
+     *
+     * @hide
+     */
+    public static final int INSTALL_QUICK = 0x00000800;
+
+    /**
      * Flag parameter for
      * {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
      * that you don't want to kill the app containing the component.  Be careful when you set this
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index bf54415..1e85dfb 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -24,6 +24,7 @@
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 
 import android.app.ActivityManager;
 import android.content.ComponentName;
@@ -39,6 +40,7 @@
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.PatternMatcher;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -622,6 +624,8 @@
     public final static int PARSE_COLLECT_CERTIFICATES = 1<<8;
     public final static int PARSE_TRUSTED_OVERLAY = 1<<9;
     public final static int PARSE_ENFORCE_CODE = 1<<10;
+    // TODO: fix b/25118622; remove this entirely once signature processing is quick
+    public final static int PARSE_SKIP_VERIFICATION = 1<<11;
 
     private static final Comparator<String> sSplitNameComparator = new SplitNameComparator();
 
@@ -902,6 +906,7 @@
             }
 
             pkg.volumeUuid = volumeUuid;
+            pkg.applicationInfo.volumeUuid = volumeUuid;
             pkg.baseCodePath = apkPath;
             pkg.mSignatures = null;
 
@@ -920,7 +925,6 @@
     private void parseSplitApk(Package pkg, int splitIndex, AssetManager assets, int flags)
             throws PackageParserException {
         final String apkPath = pkg.splitCodePaths[splitIndex];
-        final File apkFile = new File(apkPath);
 
         mParseError = PackageManager.INSTALL_SUCCEEDED;
         mArchiveSourcePath = apkPath;
@@ -1054,33 +1058,38 @@
 
     /**
      * Collect certificates from all the APKs described in the given package,
-     * populating {@link Package#mSignatures}. This also asserts that all APK
+     * populating {@link Package#mSignatures}.
+     * <p>Depending upon the parser flags, this may also asserts that all APK
      * contents are signed correctly and consistently.
      */
-    public void collectCertificates(Package pkg, int flags) throws PackageParserException {
+    public void collectCertificates(Package pkg, int parseFlags) throws PackageParserException {
         pkg.mCertificates = null;
         pkg.mSignatures = null;
         pkg.mSigningKeys = null;
 
-        collectCertificates(pkg, new File(pkg.baseCodePath), flags);
+        collectCertificates(pkg, new File(pkg.baseCodePath), parseFlags);
 
         if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
             for (String splitCodePath : pkg.splitCodePaths) {
-                collectCertificates(pkg, new File(splitCodePath), flags);
+                collectCertificates(pkg, new File(splitCodePath), parseFlags);
             }
         }
     }
 
-    private static void collectCertificates(Package pkg, File apkFile, int flags)
+    private static void collectCertificates(Package pkg, File apkFile, int parseFlags)
             throws PackageParserException {
-        final boolean requireCode = ((flags & PARSE_ENFORCE_CODE) != 0)
+        final boolean requireCode = ((parseFlags & PARSE_ENFORCE_CODE) != 0)
                 && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0);
         final String apkPath = apkFile.getAbsolutePath();
+        final boolean skipVerification = Build.IS_DEBUGGABLE
+                && ((parseFlags & PARSE_SKIP_VERIFICATION) != 0);
 
-        boolean codeFound = false;
+        boolean codeFound = skipVerification;
         StrictJarFile jarFile = null;
         try {
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor");
             jarFile = new StrictJarFile(apkPath);
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
             // Always verify manifest, regardless of source
             final ZipEntry manifestEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
@@ -1089,11 +1098,12 @@
                         "Package " + apkPath + " has no manifest");
             }
 
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "buildVerifyList");
             final List<ZipEntry> toVerify = new ArrayList<>();
             toVerify.add(manifestEntry);
 
             // If we're parsing an untrusted package, verify all contents
-            if ((flags & PARSE_IS_SYSTEM) == 0) {
+            if (!skipVerification && (parseFlags & PARSE_IS_SYSTEM) == 0) {
                 final Iterator<ZipEntry> i = jarFile.iterator();
                 while (i.hasNext()) {
                     final ZipEntry entry = i.next();
@@ -1110,6 +1120,7 @@
                     toVerify.add(entry);
                 }
             }
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
             if (!codeFound && requireCode) {
                 throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
@@ -1119,6 +1130,7 @@
             // Verify that entries are signed consistently with the first entry
             // we encountered. Note that for splits, certificates may have
             // already been populated during an earlier parse of a base APK.
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyEntries");
             for (ZipEntry entry : toVerify) {
                 final Certificate[][] entryCerts = loadCertificates(jarFile, entry);
                 if (ArrayUtils.isEmpty(entryCerts)) {
@@ -1135,6 +1147,9 @@
                     for (int i=0; i < entryCerts.length; i++) {
                         pkg.mSigningKeys.add(entryCerts[i][0].getPublicKey());
                     }
+                    if (skipVerification) {
+                        break;
+                    }
                 } else {
                     if (!Signature.areExactMatch(pkg.mSignatures, entrySignatures)) {
                         throw new PackageParserException(
@@ -1144,6 +1159,7 @@
                     }
                 }
             }
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         } catch (GeneralSecurityException e) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
                     "Failed to collect certificates from " + apkPath, e);
@@ -1199,7 +1215,8 @@
             if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
                 // TODO: factor signature related items out of Package object
                 final Package tempPkg = new Package(null);
-                collectCertificates(tempPkg, apkFile, 0);
+                // TODO: fix b/25118622; pass in '0' for parse flags
+                collectCertificates(tempPkg, apkFile, flags & PARSE_SKIP_VERIFICATION);
                 signatures = tempPkg.mSignatures;
             } else {
                 signatures = null;
@@ -1906,8 +1923,7 @@
         return pkg;
     }
 
-    private FeatureInfo parseUsesFeature(Resources res, AttributeSet attrs)
-            throws XmlPullParserException, IOException {
+    private FeatureInfo parseUsesFeature(Resources res, AttributeSet attrs) {
         FeatureInfo fi = new FeatureInfo();
         TypedArray sa = res.obtainAttributes(attrs,
                 com.android.internal.R.styleable.AndroidManifestUsesFeature);
@@ -3960,7 +3976,7 @@
 
     private boolean parseAllMetaData(Resources res,
             XmlPullParser parser, AttributeSet attrs, String tag,
-            Component outInfo, String[] outError)
+            Component<?> outInfo, String[] outError)
             throws XmlPullParserException, IOException {
         int outerDepth = parser.getDepth();
         int type;
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index c6ae14e..174291e 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -61,6 +61,8 @@
     // Registers an input devices changed listener.
     void registerInputDevicesChangedListener(IInputDevicesChangedListener listener);
 
+    // Queries whether the device is currently in tablet mode
+    int isInTabletMode();
     // Registers a tablet mode change listener
     void registerTabletModeChangedListener(ITabletModeChangedListener listener);
 
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 3949d97..201afee 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -19,6 +19,7 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
 
+import android.annotation.IntDef;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.Context;
@@ -39,6 +40,8 @@
 import android.view.InputDevice;
 import android.view.InputEvent;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -105,13 +108,14 @@
      * of a key character map for a particular keyboard layout.  The label on the receiver
      * is used to name the collection of keyboard layouts provided by this receiver in the
      * keyboard layout settings.
-     * <pre></code>
+     * <pre><code>
      * &lt;?xml version="1.0" encoding="utf-8"?>
      * &lt;keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android">
      *     &lt;keyboard-layout android:name="keyboard_layout_english_us"
      *             android:label="@string/keyboard_layout_english_us_label"
      *             android:keyboardLayout="@raw/keyboard_layout_english_us" />
      * &lt;/keyboard-layouts>
+     * </pre></code>
      * </p><p>
      * The <code>android:name</code> attribute specifies an identifier by which
      * the keyboard layout will be known in the package.
@@ -179,6 +183,31 @@
      */
     public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2;  // see InputDispatcher.h
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({SWITCH_STATE_UNKNOWN, SWITCH_STATE_OFF, SWITCH_STATE_ON})
+    public @interface SwitchState {}
+
+    /**
+     * Switch State: Unknown.
+     *
+     * The system has yet to report a valid value for the switch.
+     * @hide
+     */
+    public static final int SWITCH_STATE_UNKNOWN = -1;
+
+    /**
+     * Switch State: Off.
+     * @hide
+     */
+    public static final int SWITCH_STATE_OFF = 0;
+
+    /**
+     * Switch State: On.
+     * @hide
+     */
+    public static final int SWITCH_STATE_ON = 1;
+
     private InputManager(IInputManager im) {
         mIm = im;
     }
@@ -340,6 +369,23 @@
     }
 
     /**
+     * Queries whether the device is in tablet mode.
+     *
+     * @return The tablet switch state which is one of {@link #SWITCH_STATE_UNKNOWN},
+     * {@link #SWITCH_STATE_OFF} or {@link #SWITCH_STATE_ON}.
+     * @hide
+     */
+    @SwitchState
+    public int isInTabletMode() {
+        try {
+            return mIm.isInTabletMode();
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Could not get tablet mode state", ex);
+            return SWITCH_STATE_UNKNOWN;
+        }
+    }
+
+    /**
      * Register a tablet mode changed listener.
      *
      * @param listener The listener to register.
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index b777e8c..dc2aa3a 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -1,12 +1,12 @@
 /*
  * Copyright (C) 2008-2009 Google Inc.
- * 
+ *
  * 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
@@ -57,7 +57,7 @@
 /**
  * A view that renders a virtual {@link Keyboard}. It handles rendering of keys and
  * detecting key presses and touch movements.
- * 
+ *
  * @attr ref android.R.styleable#KeyboardView_keyBackground
  * @attr ref android.R.styleable#KeyboardView_keyPreviewLayout
  * @attr ref android.R.styleable#KeyboardView_keyPreviewOffset
@@ -73,7 +73,7 @@
      * Listener for virtual keyboard events.
      */
     public interface OnKeyboardActionListener {
-        
+
         /**
          * Called when the user presses a key. This is sent before the {@link #onKey} is called.
          * For keys that repeat, this is only called once.
@@ -81,7 +81,7 @@
          * key, the value will be zero.
          */
         void onPress(int primaryCode);
-        
+
         /**
          * Called when the user releases a key. This is sent after the {@link #onKey} is called.
          * For keys that repeat, this is only called once.
@@ -106,22 +106,22 @@
          * @param text the sequence of characters to be displayed.
          */
         void onText(CharSequence text);
-        
+
         /**
          * Called when the user quickly moves the finger from right to left.
          */
         void swipeLeft();
-        
+
         /**
          * Called when the user quickly moves the finger from left to right.
          */
         void swipeRight();
-        
+
         /**
          * Called when the user quickly moves the finger from up to down.
          */
         void swipeDown();
-        
+
         /**
          * Called when the user quickly moves the finger from down to up.
          */
@@ -131,8 +131,8 @@
     private static final boolean DEBUG = false;
     private static final int NOT_A_KEY = -1;
     private static final int[] KEY_DELETE = { Keyboard.KEYCODE_DELETE };
-    private static final int[] LONG_PRESSABLE_STATE_SET = { R.attr.state_long_pressable };   
-    
+    private static final int[] LONG_PRESSABLE_STATE_SET = { R.attr.state_long_pressable };
+
     private Keyboard mKeyboard;
     private int mCurrentKeyIndex = NOT_A_KEY;
     private int mLabelTextSize;
@@ -141,7 +141,7 @@
     private float mShadowRadius;
     private int mShadowColor;
     private float mBackgroundDimAmount;
-    
+
     private TextView mPreviewText;
     private PopupWindow mPreviewPopup;
     private int mPreviewTextSizeLarge;
@@ -162,7 +162,7 @@
 
     /** Listener for {@link OnKeyboardActionListener}. */
     private OnKeyboardActionListener mKeyboardActionListener;
-    
+
     private static final int MSG_SHOW_PREVIEW = 1;
     private static final int MSG_REMOVE_PREVIEW = 2;
     private static final int MSG_REPEAT = 3;
@@ -171,7 +171,7 @@
     private static final int DELAY_BEFORE_PREVIEW = 0;
     private static final int DELAY_AFTER_PREVIEW = 70;
     private static final int DEBOUNCE_TIME = 70;
-    
+
     private int mVerticalCorrection;
     private int mProximityThreshold;
 
@@ -187,10 +187,10 @@
     private int mStartY;
 
     private boolean mProximityCorrectOn;
-    
+
     private Paint mPaint;
     private Rect mPadding;
-    
+
     private long mDownTime;
     private long mLastMoveTime;
     private int mLastKey;
@@ -253,28 +253,7 @@
     /** Whether the requirement of a headset to hear passwords if accessibility is enabled is announced. */
     private boolean mHeadsetRequiredToHearPasswordsAnnounced;
 
-    Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_SHOW_PREVIEW:
-                    showKey(msg.arg1);
-                    break;
-                case MSG_REMOVE_PREVIEW:
-                    mPreviewText.setVisibility(INVISIBLE);
-                    break;
-                case MSG_REPEAT:
-                    if (repeatKey()) {
-                        Message repeat = Message.obtain(this, MSG_REPEAT);
-                        sendMessageDelayed(repeat, REPEAT_INTERVAL);                        
-                    }
-                    break;
-                case MSG_LONGPRESS:
-                    openPopupIfRequired((MotionEvent) msg.obj);
-                    break;
-            }
-        }
-    };
+    Handler mHandler;
 
     public KeyboardView(Context context, AttributeSet attrs) {
         this(context, attrs, com.android.internal.R.attr.keyboardViewStyle);
@@ -298,7 +277,7 @@
         int keyTextSize = 0;
 
         int n = a.getIndexCount();
-        
+
         for (int i = 0; i < n; i++) {
             int attr = a.getIndex(i);
 
@@ -338,7 +317,7 @@
                 break;
             }
         }
-        
+
         a = mContext.obtainStyledAttributes(
                 com.android.internal.R.styleable.Theme);
         mBackgroundDimAmount = a.getFloat(android.R.styleable.Theme_backgroundDimAmount, 0.5f);
@@ -352,16 +331,16 @@
         } else {
             mShowPreview = false;
         }
-        
+
         mPreviewPopup.setTouchable(false);
-        
+
         mPopupKeyboard = new PopupWindow(context);
         mPopupKeyboard.setBackgroundDrawable(null);
         //mPopupKeyboard.setClippingEnabled(false);
-        
+
         mPopupParent = this;
         //mPredicting = true;
-        
+
         mPaint = new Paint();
         mPaint.setAntiAlias(true);
         mPaint.setTextSize(keyTextSize);
@@ -380,64 +359,94 @@
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
 
         resetMultiTap();
-        initGestureDetector();
     }
 
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        initGestureDetector();
+        if (mHandler == null) {
+            mHandler = new Handler() {
+                @Override
+                public void handleMessage(Message msg) {
+                    switch (msg.what) {
+                        case MSG_SHOW_PREVIEW:
+                            showKey(msg.arg1);
+                            break;
+                        case MSG_REMOVE_PREVIEW:
+                            mPreviewText.setVisibility(INVISIBLE);
+                            break;
+                        case MSG_REPEAT:
+                            if (repeatKey()) {
+                                Message repeat = Message.obtain(this, MSG_REPEAT);
+                                sendMessageDelayed(repeat, REPEAT_INTERVAL);
+                            }
+                            break;
+                        case MSG_LONGPRESS:
+                            openPopupIfRequired((MotionEvent) msg.obj);
+                            break;
+                    }
+                }
+            };
+        }
+    }
 
     private void initGestureDetector() {
-        mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
-            @Override
-            public boolean onFling(MotionEvent me1, MotionEvent me2, 
-                    float velocityX, float velocityY) {
-                if (mPossiblePoly) return false;
-                final float absX = Math.abs(velocityX);
-                final float absY = Math.abs(velocityY);
-                float deltaX = me2.getX() - me1.getX();
-                float deltaY = me2.getY() - me1.getY();
-                int travelX = getWidth() / 2; // Half the keyboard width
-                int travelY = getHeight() / 2; // Half the keyboard height
-                mSwipeTracker.computeCurrentVelocity(1000);
-                final float endingVelocityX = mSwipeTracker.getXVelocity();
-                final float endingVelocityY = mSwipeTracker.getYVelocity();
-                boolean sendDownKey = false;
-                if (velocityX > mSwipeThreshold && absY < absX && deltaX > travelX) {
-                    if (mDisambiguateSwipe && endingVelocityX < velocityX / 4) {
-                        sendDownKey = true;
-                    } else {
-                        swipeRight();
-                        return true;
+        if (mGestureDetector == null) {
+            mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
+                @Override
+                public boolean onFling(MotionEvent me1, MotionEvent me2,
+                        float velocityX, float velocityY) {
+                    if (mPossiblePoly) return false;
+                    final float absX = Math.abs(velocityX);
+                    final float absY = Math.abs(velocityY);
+                    float deltaX = me2.getX() - me1.getX();
+                    float deltaY = me2.getY() - me1.getY();
+                    int travelX = getWidth() / 2; // Half the keyboard width
+                    int travelY = getHeight() / 2; // Half the keyboard height
+                    mSwipeTracker.computeCurrentVelocity(1000);
+                    final float endingVelocityX = mSwipeTracker.getXVelocity();
+                    final float endingVelocityY = mSwipeTracker.getYVelocity();
+                    boolean sendDownKey = false;
+                    if (velocityX > mSwipeThreshold && absY < absX && deltaX > travelX) {
+                        if (mDisambiguateSwipe && endingVelocityX < velocityX / 4) {
+                            sendDownKey = true;
+                        } else {
+                            swipeRight();
+                            return true;
+                        }
+                    } else if (velocityX < -mSwipeThreshold && absY < absX && deltaX < -travelX) {
+                        if (mDisambiguateSwipe && endingVelocityX > velocityX / 4) {
+                            sendDownKey = true;
+                        } else {
+                            swipeLeft();
+                            return true;
+                        }
+                    } else if (velocityY < -mSwipeThreshold && absX < absY && deltaY < -travelY) {
+                        if (mDisambiguateSwipe && endingVelocityY > velocityY / 4) {
+                            sendDownKey = true;
+                        } else {
+                            swipeUp();
+                            return true;
+                        }
+                    } else if (velocityY > mSwipeThreshold && absX < absY / 2 && deltaY > travelY) {
+                        if (mDisambiguateSwipe && endingVelocityY < velocityY / 4) {
+                            sendDownKey = true;
+                        } else {
+                            swipeDown();
+                            return true;
+                        }
                     }
-                } else if (velocityX < -mSwipeThreshold && absY < absX && deltaX < -travelX) {
-                    if (mDisambiguateSwipe && endingVelocityX > velocityX / 4) {
-                        sendDownKey = true;
-                    } else {
-                        swipeLeft();
-                        return true;
-                    }
-                } else if (velocityY < -mSwipeThreshold && absX < absY && deltaY < -travelY) {
-                    if (mDisambiguateSwipe && endingVelocityY > velocityY / 4) {
-                        sendDownKey = true;
-                    } else {
-                        swipeUp();
-                        return true;
-                    }
-                } else if (velocityY > mSwipeThreshold && absX < absY / 2 && deltaY > travelY) {
-                    if (mDisambiguateSwipe && endingVelocityY < velocityY / 4) {
-                        sendDownKey = true;
-                    } else {
-                        swipeDown();
-                        return true;
-                    }
-                }
 
-                if (sendDownKey) {
-                    detectAndSendKey(mDownKey, mStartX, mStartY, me1.getEventTime());
+                    if (sendDownKey) {
+                        detectAndSendKey(mDownKey, mStartX, mStartY, me1.getEventTime());
+                    }
+                    return false;
                 }
-                return false;
-            }
-        });
+            });
 
-        mGestureDetector.setIsLongpressEnabled(false);
+            mGestureDetector.setIsLongpressEnabled(false);
+        }
     }
 
     public void setOnKeyboardActionListener(OnKeyboardActionListener listener) {
@@ -487,7 +496,7 @@
     public Keyboard getKeyboard() {
         return mKeyboard;
     }
-    
+
     /**
      * Sets the state of the shift key of the keyboard, if any.
      * @param shifted whether or not to enable the state of the shift key
@@ -520,7 +529,7 @@
 
     /**
      * Enables or disables the key feedback popup. This is a popup that shows a magnified
-     * version of the depressed key. By default the preview is enabled. 
+     * version of the depressed key. By default the preview is enabled.
      * @param previewEnabled whether or not to enable the key feedback popup
      * @see #isPreviewEnabled()
      */
@@ -536,14 +545,14 @@
     public boolean isPreviewEnabled() {
         return mShowPreview;
     }
-    
+
     public void setVerticalCorrection(int verticalOffset) {
-        
+
     }
     public void setPopupParent(View v) {
         mPopupParent = v;
     }
-    
+
     public void setPopupOffset(int x, int y) {
         mMiniKeyboardOffsetX = x;
         mMiniKeyboardOffsetY = y;
@@ -569,9 +578,9 @@
         return mProximityCorrectOn;
     }
 
-    /** 
+    /**
      * Popup keyboard close button clicked.
-     * @hide 
+     * @hide
      */
     public void onClick(View v) {
         dismissPopupKeyboard();
@@ -654,9 +663,9 @@
         }
         final Canvas canvas = mCanvas;
         canvas.clipRect(mDirtyRect, Op.REPLACE);
-        
+
         if (mKeyboard == null) return;
-        
+
         final Paint paint = mPaint;
         final Drawable keyBackground = mKeyBackground;
         final Rect clipRegion = mClipRegion;
@@ -689,15 +698,15 @@
 
             // Switch the character to uppercase if shift is pressed
             String label = key.label == null? null : adjustCase(key.label).toString();
-            
+
             final Rect bounds = keyBackground.getBounds();
-            if (key.width != bounds.right || 
+            if (key.width != bounds.right ||
                     key.height != bounds.bottom) {
                 keyBackground.setBounds(0, 0, key.width, key.height);
             }
             canvas.translate(key.x + kbdPaddingLeft, key.y + kbdPaddingTop);
             keyBackground.draw(canvas);
-            
+
             if (label != null) {
                 // For characters, use large font. For labels like "Done", use small font.
                 if (label.length() > 1 && key.codes.length < 2) {
@@ -719,12 +728,12 @@
                 // Turn off drop shadow
                 paint.setShadowLayer(0, 0, 0, 0);
             } else if (key.icon != null) {
-                final int drawableX = (key.width - padding.left - padding.right 
+                final int drawableX = (key.width - padding.left - padding.right
                                 - key.icon.getIntrinsicWidth()) / 2 + padding.left;
-                final int drawableY = (key.height - padding.top - padding.bottom 
+                final int drawableY = (key.height - padding.top - padding.bottom
                         - key.icon.getIntrinsicHeight()) / 2 + padding.top;
                 canvas.translate(drawableX, drawableY);
-                key.icon.setBounds(0, 0, 
+                key.icon.setBounds(0, 0,
                         key.icon.getIntrinsicWidth(), key.icon.getIntrinsicHeight());
                 key.icon.draw(canvas);
                 canvas.translate(-drawableX, -drawableY);
@@ -748,7 +757,7 @@
             paint.setColor(0xFF00FF00);
             canvas.drawCircle((mStartX + mLastX) / 2, (mStartY + mLastY) / 2, 2, paint);
         }
-        
+
         mDrawPending = false;
         mDirtyRect.setEmpty();
     }
@@ -769,8 +778,8 @@
                 primaryIndex = nearestKeyIndices[i];
             }
 
-            if (((mProximityCorrectOn 
-                    && (dist = key.squaredDistanceFrom(x, y)) < mProximityThreshold) 
+            if (((mProximityCorrectOn
+                    && (dist = key.squaredDistanceFrom(x, y)) < mProximityThreshold)
                     || isInside)
                     && key.codes[0] > 32) {
                 // Find insertion point
@@ -779,9 +788,9 @@
                     closestKeyDist = dist;
                     closestKey = nearestKeyIndices[i];
                 }
-                
+
                 if (allKeys == null) continue;
-                
+
                 for (int j = 0; j < mDistances.length; j++) {
                     if (mDistances[j] > dist) {
                         // Make space for nCodes codes
@@ -846,11 +855,11 @@
             return adjustCase(key.label);
         }
     }
-    
+
     private void showPreview(int keyIndex) {
         int oldKeyIndex = mCurrentKeyIndex;
         final PopupWindow previewPopup = mPreviewPopup;
-        
+
         mCurrentKeyIndex = keyIndex;
         // Release the old key and press the new key
         final Key[] keys = mKeys;
@@ -884,7 +893,7 @@
             if (previewPopup.isShowing()) {
                 if (keyIndex == NOT_A_KEY) {
                     mHandler.sendMessageDelayed(mHandler
-                            .obtainMessage(MSG_REMOVE_PREVIEW), 
+                            .obtainMessage(MSG_REMOVE_PREVIEW),
                             DELAY_AFTER_PREVIEW);
                 }
             }
@@ -894,20 +903,20 @@
                     showKey(keyIndex);
                 } else {
                     mHandler.sendMessageDelayed(
-                            mHandler.obtainMessage(MSG_SHOW_PREVIEW, keyIndex, 0), 
+                            mHandler.obtainMessage(MSG_SHOW_PREVIEW, keyIndex, 0),
                             DELAY_BEFORE_PREVIEW);
                 }
             }
         }
     }
-    
+
     private void showKey(final int keyIndex) {
         final PopupWindow previewPopup = mPreviewPopup;
         final Key[] keys = mKeys;
         if (keyIndex < 0 || keyIndex >= mKeys.length) return;
         Key key = keys[keyIndex];
         if (key.icon != null) {
-            mPreviewText.setCompoundDrawables(null, null, null, 
+            mPreviewText.setCompoundDrawables(null, null, null,
                     key.iconPreview != null ? key.iconPreview : key.icon);
             mPreviewText.setText(null);
         } else {
@@ -921,9 +930,9 @@
                 mPreviewText.setTypeface(Typeface.DEFAULT);
             }
         }
-        mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 
+        mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
                 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
-        int popupWidth = Math.max(mPreviewText.getMeasuredWidth(), key.width 
+        int popupWidth = Math.max(mPreviewText.getMeasuredWidth(), key.width
                 + mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight());
         final int popupHeight = mPreviewHeight;
         LayoutParams lp = mPreviewText.getLayoutParams();
@@ -969,7 +978,7 @@
         } else {
             previewPopup.setWidth(popupWidth);
             previewPopup.setHeight(popupHeight);
-            previewPopup.showAtLocation(mPopupParent, Gravity.NO_GRAVITY, 
+            previewPopup.showAtLocation(mPopupParent, Gravity.NO_GRAVITY,
                     mPopupPreviewX, mPopupPreviewY);
         }
         mPreviewText.setVisibility(VISIBLE);
@@ -1030,7 +1039,7 @@
 
     /**
      * Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient
-     * because the keyboard renders the keys to an off-screen buffer and an invalidate() only 
+     * because the keyboard renders the keys to an off-screen buffer and an invalidate() only
      * draws the cached buffer.
      * @see #invalidateKey(int)
      */
@@ -1054,10 +1063,10 @@
         }
         final Key key = mKeys[keyIndex];
         mInvalidatedKey = key;
-        mDirtyRect.union(key.x + mPaddingLeft, key.y + mPaddingTop, 
+        mDirtyRect.union(key.x + mPaddingLeft, key.y + mPaddingTop,
                 key.x + key.width + mPaddingLeft, key.y + key.height + mPaddingTop);
         onBufferDraw();
-        invalidate(key.x + mPaddingLeft, key.y + mPaddingTop, 
+        invalidate(key.x + mPaddingLeft, key.y + mPaddingTop,
                 key.x + key.width + mPaddingLeft, key.y + key.height + mPaddingTop);
     }
 
@@ -1070,7 +1079,7 @@
             return false;
         }
 
-        Key popupKey = mKeys[mCurrentKey];        
+        Key popupKey = mKeys[mCurrentKey];
         boolean result = onLongPress(popupKey);
         if (result) {
             mAbortKey = true;
@@ -1105,12 +1114,12 @@
                         mKeyboardActionListener.onKey(primaryCode, keyCodes);
                         dismissPopupKeyboard();
                     }
-                    
+
                     public void onText(CharSequence text) {
                         mKeyboardActionListener.onText(text);
                         dismissPopupKeyboard();
                     }
-                    
+
                     public void swipeLeft() { }
                     public void swipeRight() { }
                     public void swipeUp() { }
@@ -1125,7 +1134,7 @@
                 //mInputView.setSuggest(mSuggest);
                 Keyboard keyboard;
                 if (popupKey.popupCharacters != null) {
-                    keyboard = new Keyboard(getContext(), popupKeyboardId, 
+                    keyboard = new Keyboard(getContext(), popupKeyboardId,
                             popupKey.popupCharacters, -1, getPaddingLeft() + getPaddingRight());
                 } else {
                     keyboard = new Keyboard(getContext(), popupKeyboardId);
@@ -1133,9 +1142,9 @@
                 mMiniKeyboard.setKeyboard(keyboard);
                 mMiniKeyboard.setPopupParent(this);
                 mMiniKeyboardContainer.measure(
-                        MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), 
+                        MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
                         MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
-                
+
                 mMiniKeyboardCache.put(popupKey, mMiniKeyboardContainer);
             } else {
                 mMiniKeyboard = (KeyboardView) mMiniKeyboardContainer.findViewById(
@@ -1184,7 +1193,7 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent me) {
-        // Convert multi-pointer up/down events to single up/down events to 
+        // Convert multi-pointer up/down events to single up/down events to
         // deal with the typical multi-pointer behavior of two-thumb typing
         final int pointerCount = me.getPointerCount();
         final int action = me.getAction();
@@ -1250,7 +1259,7 @@
             mHandler.removeMessages(MSG_LONGPRESS);
             return true;
         }
-        
+
         // Needs to be called after the gesture detector gets a turn, as it may have
         // displayed the mini keyboard
         if (mMiniKeyboardOnScreen && action != MotionEvent.ACTION_CANCEL) {
@@ -1272,7 +1281,7 @@
                 mDownTime = me.getEventTime();
                 mLastMoveTime = mDownTime;
                 checkMultiTap(eventTime, keyIndex);
-                mKeyboardActionListener.onPress(keyIndex != NOT_A_KEY ? 
+                mKeyboardActionListener.onPress(keyIndex != NOT_A_KEY ?
                         mKeys[keyIndex].codes[0] : 0);
                 if (mCurrentKey >= 0 && mKeys[mCurrentKey].repeatable) {
                     mRepeatKeyIndex = mCurrentKey;
@@ -1371,11 +1380,11 @@
         detectAndSendKey(mCurrentKey, key.x, key.y, mLastTapTime);
         return true;
     }
-    
+
     protected void swipeRight() {
         mKeyboardActionListener.swipeRight();
     }
-    
+
     protected void swipeLeft() {
         mKeyboardActionListener.swipeLeft();
     }
@@ -1393,7 +1402,7 @@
             mPreviewPopup.dismiss();
         }
         removeMessages();
-        
+
         dismissPopupKeyboard();
         mBuffer = null;
         mCanvas = null;
@@ -1434,7 +1443,7 @@
         mLastTapTime = -1;
         mInMultiTap = false;
     }
-    
+
     private void checkMultiTap(long eventTime, int keyIndex) {
         if (keyIndex == NOT_A_KEY) return;
         Key key = mKeys[keyIndex];
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 444548f..ad9058f 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -901,8 +901,12 @@
      * Tells the underlying networking system that the caller wants to
      * begin using the named feature. The interpretation of {@code feature}
      * is completely up to each networking implementation.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
+     *
      * @param networkType specifies which network the request pertains to
      * @param feature the name of the feature to be used
      * @return an integer value representing the outcome of the request.
@@ -952,8 +956,12 @@
      * Tells the underlying networking system that the caller is finished
      * using the named feature. The interpretation of {@code feature}
      * is completely up to each networking implementation.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
+     *
      * @param networkType specifies which network the request pertains to
      * @param feature the name of the feature that is no longer needed
      * @return an integer value representing the outcome of the request.
@@ -1339,8 +1347,12 @@
      * Ensure that a network route exists to deliver traffic to the specified
      * host via the specified network interface. An attempt to add a route that
      * already exists is ignored, but treated as successful.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
+     *
      * @param networkType the type of the network over which traffic to the specified
      * host is to be routed
      * @param hostAddress the IP address of the host to which the route is desired
@@ -1360,8 +1372,12 @@
      * Ensure that a network route exists to deliver traffic to the specified
      * host via the specified network interface. An attempt to add a route that
      * already exists is ignored, but treated as successful.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
+     *
      * @param networkType the type of the network over which traffic to the specified
      * host is to be routed
      * @param hostAddress the IP address of the host to which the route is desired
@@ -1561,6 +1577,13 @@
         return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
     }
 
+    /** {@hide} */
+    public static final void enforceChangePermission(Context context) {
+        int uid = Binder.getCallingUid();
+        Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
+                .getPackageNameForUid(context, uid), true /* throwException */);
+    }
+
     /** {@hide */
     public static final void enforceTetherChangePermission(Context context) {
         if (context.getResources().getStringArray(
@@ -1571,8 +1594,8 @@
                     android.Manifest.permission.CONNECTIVITY_INTERNAL, "ConnectivityService");
         } else {
             int uid = Binder.getCallingUid();
-            Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
-                    .getPackageNameForUid(context, uid), true);
+            Settings.checkAndNoteWriteSettingsOperation(context, uid, Settings
+                    .getPackageNameForUid(context, uid), true /* throwException */);
         }
     }
 
@@ -1677,8 +1700,11 @@
      * allowed between the tethered devices and this device, though upstream net
      * access will of course fail until an upstream network interface becomes
      * active.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
      *
      * @param iface the interface name to tether.
      * @return error a {@code TETHER_ERROR} value indicating success or failure type
@@ -1695,8 +1721,11 @@
 
     /**
      * Stop tethering the named interface.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
      *
      * @param iface the interface name to untether.
      * @return error a {@code TETHER_ERROR} value indicating success or failure type
@@ -1796,8 +1825,11 @@
      * attempt to switch to Rndis and subsequently tether the resulting
      * interface on {@code true} or turn off tethering and switch off
      * Rndis on {@code false}.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
      *
      * @param enable a boolean - {@code true} to enable tethering
      * @return error a {@code TETHER_ERROR} value indicating success or failure type
@@ -2467,8 +2499,11 @@
      * network may never attain, and whether a network will attain these states
      * is unknown prior to bringing up the network so the framework does not
      * know how to go about satisfing a request with these capabilities.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
      *
      * @param request {@link NetworkRequest} describing this request.
      * @param networkCallback The {@link NetworkCallback} to be utilized for this
@@ -2490,8 +2525,12 @@
      * network is not found within the given time (in milliseconds) the
      * {@link NetworkCallback#unavailable} callback is called.  The request must
      * still be released normally by calling {@link releaseNetworkRequest}.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
+     *
      * @param request {@link NetworkRequest} describing this request.
      * @param networkCallback The callbacks to be utilized for this request.  Note
      *                        the callbacks must not be shared - they uniquely specify
@@ -2564,8 +2603,12 @@
      * network may never attain, and whether a network will attain these states
      * is unknown prior to bringing up the network so the framework does not
      * know how to go about satisfing a request with these capabilities.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
+     *
      * @param request {@link NetworkRequest} describing this request.
      * @param operation Action to perform when the network is available (corresponds
      *                  to the {@link NetworkCallback#onAvailable} call.  Typically
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 57eef83..b7a411e 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -288,7 +288,8 @@
         } else {
             final boolean matchesType = (sForceAllNetworkTypes
                     || contains(DATA_USAGE_NETWORK_TYPES, ident.mType));
-            return matchesType && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId);
+            return matchesType && !ArrayUtils.isEmpty(mMatchSubscriberIds)
+                    && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId);
         }
     }
 
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 1273772b..5852f5f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.IntegerRes;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -42,6 +43,8 @@
 import java.util.Map;
 import java.util.Set;
 
+import dalvik.system.VMRuntime;
+
 /**
  * Container for a message (data and object references) that can
  * be sent through an IBinder.  A Parcel can contain both flattened data
@@ -193,6 +196,7 @@
      * indicating that we're responsible for its lifecycle.
      */
     private boolean mOwnsNativeParcelObject;
+    private long mNativeSize;
 
     private RuntimeException mStack;
 
@@ -244,7 +248,7 @@
     private static native int nativeDataAvail(long nativePtr);
     private static native int nativeDataPosition(long nativePtr);
     private static native int nativeDataCapacity(long nativePtr);
-    private static native void nativeSetDataSize(long nativePtr, int size);
+    private static native long nativeSetDataSize(long nativePtr, int size);
     private static native void nativeSetDataPosition(long nativePtr, int pos);
     private static native void nativeSetDataCapacity(long nativePtr, int size);
 
@@ -259,7 +263,7 @@
     private static native void nativeWriteDouble(long nativePtr, double val);
     private static native void nativeWriteString(long nativePtr, String val);
     private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
-    private static native void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
+    private static native long nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
 
     private static native byte[] nativeCreateByteArray(long nativePtr);
     private static native byte[] nativeReadBlob(long nativePtr);
@@ -272,13 +276,13 @@
     private static native FileDescriptor nativeReadFileDescriptor(long nativePtr);
 
     private static native long nativeCreate();
-    private static native void nativeFreeBuffer(long nativePtr);
+    private static native long nativeFreeBuffer(long nativePtr);
     private static native void nativeDestroy(long nativePtr);
 
     private static native byte[] nativeMarshall(long nativePtr);
-    private static native void nativeUnmarshall(
+    private static native long nativeUnmarshall(
             long nativePtr, byte[] data, int offset, int length);
-    private static native void nativeAppendFrom(
+    private static native long nativeAppendFrom(
             long thisNativePtr, long otherNativePtr, int offset, int length);
     private static native boolean nativeHasFileDescriptors(long nativePtr);
     private static native void nativeWriteInterfaceToken(long nativePtr, String interfaceName);
@@ -390,7 +394,7 @@
      * @param size The new number of bytes in the Parcel.
      */
     public final void setDataSize(int size) {
-        nativeSetDataSize(mNativePtr, size);
+        updateNativeSize(nativeSetDataSize(mNativePtr, size));
     }
 
     /**
@@ -442,11 +446,11 @@
      * Set the bytes in data to be the raw bytes of this Parcel.
      */
     public final void unmarshall(byte[] data, int offset, int length) {
-        nativeUnmarshall(mNativePtr, data, offset, length);
+        updateNativeSize(nativeUnmarshall(mNativePtr, data, offset, length));
     }
 
     public final void appendFrom(Parcel parcel, int offset, int length) {
-        nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length);
+        updateNativeSize(nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length));
     }
 
     /**
@@ -599,7 +603,24 @@
      * if {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set.</p>
      */
     public final void writeFileDescriptor(FileDescriptor val) {
-        nativeWriteFileDescriptor(mNativePtr, val);
+        updateNativeSize(nativeWriteFileDescriptor(mNativePtr, val));
+    }
+
+    private void updateNativeSize(long newNativeSize) {
+        if (mOwnsNativeParcelObject) {
+            if (newNativeSize > Integer.MAX_VALUE) {
+                newNativeSize = Integer.MAX_VALUE;
+            }
+            if (newNativeSize != mNativeSize) {
+                int delta = (int) (newNativeSize - mNativeSize);
+                if (delta > 0) {
+                    VMRuntime.getRuntime().registerNativeAllocation(delta);
+                } else {
+                    VMRuntime.getRuntime().registerNativeFree(-delta);
+                }
+                mNativeSize = newNativeSize;
+            }
+        }
     }
 
     /**
@@ -2545,7 +2566,7 @@
 
     private void freeBuffer() {
         if (mOwnsNativeParcelObject) {
-            nativeFreeBuffer(mNativePtr);
+            updateNativeSize(nativeFreeBuffer(mNativePtr));
         }
     }
 
@@ -2553,6 +2574,7 @@
         if (mNativePtr != 0) {
             if (mOwnsNativeParcelObject) {
                 nativeDestroy(mNativePtr);
+                updateNativeSize(0);
             }
             mNativePtr = 0;
         }
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 41de579..4535572 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -341,6 +341,10 @@
         } finally {
             uncryptFile.close();
         }
+        // UNCRYPT_FILE needs to be readable by system server on bootup.
+        if (!UNCRYPT_FILE.setReadable(true, false)) {
+            Log.e(TAG, "Error setting readable for " + UNCRYPT_FILE.getCanonicalPath());
+        }
         Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");
 
         // If the package is on the /data partition, write the block map file
@@ -501,6 +505,25 @@
             Log.e(TAG, "Error reading recovery log", e);
         }
 
+        if (UNCRYPT_FILE.exists()) {
+            String filename = null;
+            try {
+                filename = FileUtils.readTextFile(UNCRYPT_FILE, 0, null);
+            } catch (IOException e) {
+                Log.e(TAG, "Error reading uncrypt file", e);
+            }
+
+            // Remove the OTA package on /data that has been (possibly
+            // partially) processed. (Bug: 24973532)
+            if (filename != null && filename.startsWith("/data")) {
+                if (UNCRYPT_FILE.delete()) {
+                    Log.i(TAG, "Deleted: " + filename);
+                } else {
+                    Log.e(TAG, "Can't delete: " + filename);
+                }
+            }
+        }
+
         // Delete everything in RECOVERY_DIR except those beginning
         // with LAST_PREFIX
         String[] names = RECOVERY_DIR.list();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d601831..11effd0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1434,25 +1434,6 @@
     }
 
     /**
-     * An app can use this method to check if it is currently allowed to change the network
-     * state. In order to be allowed to do so, an app must first declare either the
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} or
-     * {@link android.Manifest.permission#WRITE_SETTINGS} permission in its manifest. If it
-     * is currently disallowed, it can prompt the user to grant it this capability through a
-     * management UI by sending an Intent with action
-     * {@link android.provider.Settings#ACTION_MANAGE_WRITE_SETTINGS}.
-     *
-     * @param context A context
-     * @return true if the calling app can change the state of network, false otherwise.
-     * @hide
-     */
-    public static boolean canChangeNetworkState(Context context) {
-        int uid = Binder.getCallingUid();
-        return Settings.isCallingPackageAllowedToChangeNetworkState(context, uid, Settings
-                .getPackageNameForUid(context, uid), false);
-    }
-
-    /**
      * System settings, containing miscellaneous system preferences.  This
      * table holds simple name/value pairs.  There are convenience
      * functions for accessing individual settings entries.
@@ -8384,7 +8365,7 @@
      * write/modify system settings, as the condition differs for pre-M, M+, and
      * privileged/preinstalled apps. If the provided uid does not match the
      * callingPackage, a negative result will be returned. The caller is expected to have
-     * either WRITE_SETTINGS or CHANGE_NETWORK_STATE permission declared.
+     * the WRITE_SETTINGS permission declared.
      *
      * Note: if the check is successful, the operation of this app will be updated to the
      * current time.
@@ -8400,31 +8381,22 @@
     /**
      * Performs a strict and comprehensive check of whether a calling package is allowed to
      * change the state of network, as the condition differs for pre-M, M+, and
-     * privileged/preinstalled apps. If the provided uid does not match the
-     * callingPackage, a negative result will be returned. The caller is expected to have
-     * either of CHANGE_NETWORK_STATE or WRITE_SETTINGS permission declared.
-     * @hide
-     */
-    public static boolean isCallingPackageAllowedToChangeNetworkState(Context context, int uid,
-            String callingPackage, boolean throwException) {
-        return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid,
-                callingPackage, throwException, AppOpsManager.OP_WRITE_SETTINGS,
-                PM_CHANGE_NETWORK_STATE, false);
-    }
-
-    /**
-     * Performs a strict and comprehensive check of whether a calling package is allowed to
-     * change the state of network, as the condition differs for pre-M, M+, and
-     * privileged/preinstalled apps. If the provided uid does not match the
-     * callingPackage, a negative result will be returned. The caller is expected to have
-     * either CHANGE_NETWORK_STATE or WRITE_SETTINGS permission declared.
+     * privileged/preinstalled apps. The caller is expected to have either the
+     * CHANGE_NETWORK_STATE or the WRITE_SETTINGS permission declared. Either of these
+     * permissions allow changing network state; WRITE_SETTINGS is a runtime permission and
+     * can be revoked, but (except in M, excluding M MRs), CHANGE_NETWORK_STATE is a normal
+     * permission and cannot be revoked. See http://b/23597341
      *
-     * Note: if the check is successful, the operation of this app will be updated to the
-     * current time.
+     * Note: if the check succeeds because the application holds WRITE_SETTINGS, the operation
+     * of this app will be updated to the current time.
      * @hide
      */
     public static boolean checkAndNoteChangeNetworkStateOperation(Context context, int uid,
             String callingPackage, boolean throwException) {
+        if (context.checkCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE)
+                == PackageManager.PERMISSION_GRANTED) {
+            return true;
+        }
         return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid,
                 callingPackage, throwException, AppOpsManager.OP_WRITE_SETTINGS,
                 PM_CHANGE_NETWORK_STATE, true);
diff --git a/core/java/android/util/PathParser.java b/core/java/android/util/PathParser.java
index 954dcfb..cebb8f0 100644
--- a/core/java/android/util/PathParser.java
+++ b/core/java/android/util/PathParser.java
@@ -660,7 +660,7 @@
             // and http://www.spaceroots.org/documents/ellipse/node22.html
 
             // Maximum of 45 degrees per cubic Bezier segment
-            int numSegments = Math.abs((int) Math.ceil(sweep * 4 / Math.PI));
+            int numSegments = (int) Math.ceil(Math.abs(sweep * 4 / Math.PI));
 
             double eta1 = start;
             double cosTheta = Math.cos(theta);
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 1c20cab..55cf56f 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -790,8 +790,14 @@
     public static final int KEYCODE_MEDIA_STEP_BACKWARD = 275;
     /** Key code constant: put device to sleep unless a wakelock is held. */
     public static final int KEYCODE_SOFT_SLEEP = 276;
+    /** Key code constant: Cut key. */
+    public static final int KEYCODE_CUT = 277;
+    /** Key code constant: Copy key. */
+    public static final int KEYCODE_COPY = 278;
+    /** Key code constant: Paste key. */
+    public static final int KEYCODE_PASTE = 279;
 
-    private static final int LAST_KEYCODE = KEYCODE_SOFT_SLEEP;
+    private static final int LAST_KEYCODE = KEYCODE_PASTE;
 
     // NOTE: If you add a new keycode here you must also add it to:
     //  isSystem()
diff --git a/core/java/android/widget/AdapterViewFlipper.java b/core/java/android/widget/AdapterViewFlipper.java
index a105b40..18d7470 100644
--- a/core/java/android/widget/AdapterViewFlipper.java
+++ b/core/java/android/widget/AdapterViewFlipper.java
@@ -21,7 +21,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.TypedArray;
-import android.os.Handler;
 import android.os.Message;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -112,7 +111,7 @@
         // home screen. Therefore, we register the receiver as the current
         // user not the one the context is for.
         getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
-                filter, null, mHandler);
+                filter, null, getHandler());
 
 
         if (mAutoStart) {
@@ -194,9 +193,8 @@
        // if the flipper is currently flipping automatically, and showNext() is called
        // we should we should make sure to reset the timer
        if (mRunning) {
-           mHandler.removeMessages(FLIP_MSG);
-           Message msg = mHandler.obtainMessage(FLIP_MSG);
-           mHandler.sendMessageDelayed(msg, mFlipInterval);
+           removeCallbacks(mFlipRunnable);
+           postDelayed(mFlipRunnable, mFlipInterval);
        }
        super.showNext();
    }
@@ -210,9 +208,8 @@
        // if the flipper is currently flipping automatically, and showPrevious() is called
        // we should we should make sure to reset the timer
        if (mRunning) {
-           mHandler.removeMessages(FLIP_MSG);
-           Message msg = mHandler.obtainMessage(FLIP_MSG);
-           mHandler.sendMessageDelayed(msg, mFlipInterval);
+           removeCallbacks(mFlipRunnable);
+           postDelayed(mFlipRunnable, mFlipInterval);
        }
        super.showPrevious();
    }
@@ -241,10 +238,9 @@
         if (running != mRunning) {
             if (running) {
                 showOnly(mWhichChild, flipNow);
-                Message msg = mHandler.obtainMessage(FLIP_MSG);
-                mHandler.sendMessageDelayed(msg, mFlipInterval);
+                postDelayed(mFlipRunnable, mFlipInterval);
             } else {
-                mHandler.removeMessages(FLIP_MSG);
+                removeCallbacks(mFlipRunnable);
             }
             mRunning = running;
         }
@@ -277,15 +273,11 @@
         return mAutoStart;
     }
 
-    private final int FLIP_MSG = 1;
-
-    private final Handler mHandler = new Handler() {
+    private final Runnable mFlipRunnable = new Runnable() {
         @Override
-        public void handleMessage(Message msg) {
-            if (msg.what == FLIP_MSG) {
-                if (mRunning) {
-                    showNext();
-                }
+        public void run() {
+            if (mRunning) {
+                showNext();
             }
         }
     };
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 45eee34..7f5e2133 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -56,7 +56,6 @@
 
     private boolean mAttached;
 
-    private final Handler mHandler = new Handler();
     private float mMinutes;
     private float mHour;
     private boolean mChanged;
@@ -121,7 +120,7 @@
             // home screen. Therefore, we register the receiver as the current
             // user not the one the context is for.
             getContext().registerReceiverAsUser(mIntentReceiver,
-                    android.os.Process.myUserHandle(), filter, null, mHandler);
+                    android.os.Process.myUserHandle(), filter, null, getHandler());
         }
 
         // NOTE: It's safe to do these after registering the receiver since the receiver always runs
diff --git a/core/java/android/widget/Chronometer.java b/core/java/android/widget/Chronometer.java
index ebb54ff..4d707e3 100644
--- a/core/java/android/widget/Chronometer.java
+++ b/core/java/android/widget/Chronometer.java
@@ -73,8 +73,6 @@
     private OnChronometerTickListener mOnChronometerTickListener;
     private StringBuilder mRecycle = new StringBuilder(8);
     
-    private static final int TICK_WHAT = 2;
-    
     /**
      * Initialize this Chronometer object.
      * Sets the base to the current time.
@@ -259,20 +257,21 @@
             if (running) {
                 updateText(SystemClock.elapsedRealtime());
                 dispatchChronometerTick();
-                mHandler.sendMessageDelayed(Message.obtain(mHandler, TICK_WHAT), 1000);
+                postDelayed(mTickRunnable, 1000);
             } else {
-                mHandler.removeMessages(TICK_WHAT);
+                removeCallbacks(mTickRunnable);
             }
             mRunning = running;
         }
     }
-    
-    private Handler mHandler = new Handler() {
-        public void handleMessage(Message m) {
+
+    private final Runnable mTickRunnable = new Runnable() {
+        @Override
+        public void run() {
             if (mRunning) {
                 updateText(SystemClock.elapsedRealtime());
                 dispatchChronometerTick();
-                sendMessageDelayed(Message.obtain(this, TICK_WHAT), 1000);
+                postDelayed(mTickRunnable, 1000);
             }
         }
     };
diff --git a/core/java/android/widget/DigitalClock.java b/core/java/android/widget/DigitalClock.java
index 9e442f9..1df1643 100644
--- a/core/java/android/widget/DigitalClock.java
+++ b/core/java/android/widget/DigitalClock.java
@@ -60,18 +60,18 @@
         if (mCalendar == null) {
             mCalendar = Calendar.getInstance();
         }
-
-        mFormatChangeObserver = new FormatChangeObserver();
-        getContext().getContentResolver().registerContentObserver(
-                Settings.System.CONTENT_URI, true, mFormatChangeObserver);
-
-        setFormat();
     }
 
     @Override
     protected void onAttachedToWindow() {
         mTickerStopped = false;
         super.onAttachedToWindow();
+
+        mFormatChangeObserver = new FormatChangeObserver();
+        getContext().getContentResolver().registerContentObserver(
+                Settings.System.CONTENT_URI, true, mFormatChangeObserver);
+        setFormat();
+
         mHandler = new Handler();
 
         /**
@@ -95,6 +95,8 @@
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         mTickerStopped = true;
+        getContext().getContentResolver().unregisterContentObserver(
+                mFormatChangeObserver);
     }
 
     private void setFormat() {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 018cc02..2fabe33 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -16,12 +16,6 @@
 
 package android.widget;
 
-import java.text.BreakIterator;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-
 import android.R;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
@@ -45,7 +39,6 @@
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.ParcelableParcel;
@@ -113,6 +106,12 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.EditableInputConnection;
 
+import java.text.BreakIterator;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
 
 /**
  * Helper class used by TextView to handle editable text views.
@@ -1005,8 +1004,7 @@
         }
 
         if (!handled && mTextActionMode != null) {
-            // TODO: Fix dragging in extracted mode.
-            if (touchPositionIsInSelection() && !mTextView.isInExtractedMode()) {
+            if (touchPositionIsInSelection()) {
                 // Start a drag
                 final int start = mTextView.getSelectionStart();
                 final int end = mTextView.getSelectionEnd();
@@ -2072,14 +2070,14 @@
         if (shouldBlink()) {
             mShowCursor = SystemClock.uptimeMillis();
             if (mBlink == null) mBlink = new Blink();
-            mBlink.removeCallbacks(mBlink);
-            mBlink.postAtTime(mBlink, mShowCursor + BLINK);
+            mTextView.removeCallbacks(mBlink);
+            mTextView.postDelayed(mBlink, BLINK);
         } else {
-            if (mBlink != null) mBlink.removeCallbacks(mBlink);
+            if (mBlink != null) mTextView.removeCallbacks(mBlink);
         }
     }
 
-    private class Blink extends Handler implements Runnable {
+    private class Blink implements Runnable {
         private boolean mCancelled;
 
         public void run() {
@@ -2087,20 +2085,20 @@
                 return;
             }
 
-            removeCallbacks(Blink.this);
+            mTextView.removeCallbacks(this);
 
             if (shouldBlink()) {
                 if (mTextView.getLayout() != null) {
                     mTextView.invalidateCursorPath();
                 }
 
-                postAtTime(this, SystemClock.uptimeMillis() + BLINK);
+                mTextView.postDelayed(this, BLINK);
             }
         }
 
         void cancel() {
             if (!mCancelled) {
-                removeCallbacks(Blink.this);
+                mTextView.removeCallbacks(this);
                 mCancelled = true;
             }
         }
@@ -4262,10 +4260,14 @@
             positionAtCursorOffset(offset, false);
         }
 
+        /**
+         * @param offset Cursor offset. Must be in [-1, length].
+         * @param parentScrolled If the parent has been scrolled or not.
+         */
         @Override
         protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
             super.positionAtCursorOffset(offset, parentScrolled);
-            mInWord = !getWordIteratorWithText().isBoundary(offset);
+            mInWord = (offset != -1) && !getWordIteratorWithText().isBoundary(offset);
         }
 
         @Override
@@ -4498,10 +4500,14 @@
             positionAtCursorOffset(offset, false);
         }
 
+        /**
+         * @param offset Cursor offset. Must be in [-1, length].
+         * @param parentScrolled If the parent has been scrolled or not.
+         */
         @Override
         protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
             super.positionAtCursorOffset(offset, parentScrolled);
-            mInWord = !getWordIteratorWithText().isBoundary(offset);
+            mInWord = (offset != -1) && !getWordIteratorWithText().isBoundary(offset);
         }
 
         @Override
@@ -4868,9 +4874,8 @@
                         mEndHandle.showAtLocation(endOffset);
 
                         // No longer the first dragging motion, reset.
-                        if (!(mTextView.isInExtractedMode())) {
-                            startSelectionActionMode();
-                        }
+                        startSelectionActionMode();
+
                         mDragAcceleratorActive = false;
                         mStartOffset = -1;
                         mSwitchedLines = false;
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index b187c1c..9ebbe36 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -201,9 +201,6 @@
 
     public Gallery(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        
-        mGestureDetector = new GestureDetector(context, this);
-        mGestureDetector.setIsLongpressEnabled(true);
 
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, com.android.internal.R.styleable.Gallery, defStyleAttr, defStyleRes);
@@ -236,6 +233,16 @@
         mGroupFlags |= FLAG_SUPPORT_STATIC_TRANSFORMATIONS;
     }
 
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        if (mGestureDetector == null) {
+            mGestureDetector = new GestureDetector(getContext(), this);
+            mGestureDetector.setIsLongpressEnabled(true);
+        }
+    }
+
     /**
      * Whether or not to callback on any {@link #getOnItemSelectedListener()}
      * while the items are being flinged. If false, only the final selected item
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 245c7332..6e90baf 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -232,6 +232,8 @@
                 if (w != mDrawableWidth || h != mDrawableHeight) {
                     mDrawableWidth = w;
                     mDrawableHeight = h;
+                    // updates the matrix, which is dependent on the bounds
+                    configureBounds();
                 }
             }
             /* we invalidate the whole view in this case because it's very
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index ff2f22b..8008637 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -20,8 +20,6 @@
 import android.content.res.Resources;
 import android.graphics.PixelFormat;
 import android.media.AudioManager;
-import android.os.Handler;
-import android.os.Message;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
@@ -83,8 +81,6 @@
     private boolean mShowing;
     private boolean mDragging;
     private static final int sDefaultTimeout = 3000;
-    private static final int FADE_OUT = 1;
-    private static final int SHOW_PROGRESS = 2;
     private final boolean mUseFastForward;
     private boolean mFromXml;
     private boolean mListenersSet;
@@ -373,12 +369,11 @@
         // cause the progress bar to be updated even if mShowing
         // was already true.  This happens, for example, if we're
         // paused with the progress bar showing the user hits play.
-        mHandler.sendEmptyMessage(SHOW_PROGRESS);
+        post(mShowProgress);
 
         if (timeout != 0 && !mAccessibilityManager.isTouchExplorationEnabled()) {
-            mHandler.removeMessages(FADE_OUT);
-            Message msg = mHandler.obtainMessage(FADE_OUT);
-            mHandler.sendMessageDelayed(msg, timeout);
+            removeCallbacks(mFadeOut);
+            postDelayed(mFadeOut, timeout);
         }
     }
 
@@ -395,7 +390,7 @@
 
         if (mShowing) {
             try {
-                mHandler.removeMessages(SHOW_PROGRESS);
+                removeCallbacks(mShowProgress);
                 mWindowManager.removeView(mDecor);
             } catch (IllegalArgumentException ex) {
                 Log.w("MediaController", "already removed");
@@ -404,21 +399,19 @@
         }
     }
 
-    private final Handler mHandler = new Handler() {
+    private final Runnable mFadeOut = new Runnable() {
         @Override
-        public void handleMessage(Message msg) {
-            int pos;
-            switch (msg.what) {
-                case FADE_OUT:
-                    hide();
-                    break;
-                case SHOW_PROGRESS:
-                    pos = setProgress();
-                    if (!mDragging && mShowing && mPlayer.isPlaying()) {
-                        msg = obtainMessage(SHOW_PROGRESS);
-                        sendMessageDelayed(msg, 1000 - (pos % 1000));
-                    }
-                    break;
+        public void run() {
+            hide();
+        }
+    };
+
+    private final Runnable mShowProgress = new Runnable() {
+        @Override
+        public void run() {
+            int pos = setProgress();
+            if (!mDragging && mShowing && mPlayer.isPlaying()) {
+                postDelayed(mShowProgress, 1000 - (pos % 1000));
             }
         }
     };
@@ -587,7 +580,7 @@
             // the seekbar and b) once the user is done dragging the thumb
             // we will post one of these messages to the queue again and
             // this ensures that there will be exactly one message queued up.
-            mHandler.removeMessages(SHOW_PROGRESS);
+            removeCallbacks(mShowProgress);
         }
 
         @Override
@@ -615,7 +608,7 @@
             // Ensure that progress is properly updated in the future,
             // the call to show() does not guarantee this because it is a
             // no-op if we are already showing.
-            mHandler.sendEmptyMessage(SHOW_PROGRESS);
+            post(mShowProgress);
         }
     };
 
diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java
index e241d4c..8c15cde 100644
--- a/core/java/android/widget/QuickContactBadge.java
+++ b/core/java/android/widget/QuickContactBadge.java
@@ -96,10 +96,16 @@
                 com.android.internal.R.styleable.Theme_quickContactBadgeOverlay);
         styledAttributes.recycle();
 
+        setOnClickListener(this);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
         if (!isInEditMode()) {
             mQueryHandler = new QueryHandler(mContext.getContentResolver());
         }
-        setOnClickListener(this);
     }
 
     @Override
diff --git a/core/java/android/widget/SlidingDrawer.java b/core/java/android/widget/SlidingDrawer.java
index 9c44236..a55e77d 100644
--- a/core/java/android/widget/SlidingDrawer.java
+++ b/core/java/android/widget/SlidingDrawer.java
@@ -93,7 +93,6 @@
     private static final float MAXIMUM_MAJOR_VELOCITY = 200.0f;
     private static final float MAXIMUM_ACCELERATION = 2000.0f;
     private static final int VELOCITY_UNITS = 1000;
-    private static final int MSG_ANIMATE = 1000;
     private static final int ANIMATION_FRAME_DURATION = 1000 / 60;
 
     private static final int EXPANDED_FULL_OPEN = -10001;
@@ -123,7 +122,6 @@
     private OnDrawerCloseListener mOnDrawerCloseListener;
     private OnDrawerScrollListener mOnDrawerScrollListener;
 
-    private final Handler mHandler = new SlidingHandler();
     private float mAnimatedAcceleration;
     private float mAnimatedVelocity;
     private float mAnimationPosition;
@@ -553,8 +551,8 @@
         mAnimationLastTime = now;
         mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;
         mAnimating = true;
-        mHandler.removeMessages(MSG_ANIMATE);
-        mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurrentAnimationTime);
+        removeCallbacks(mSlidingRunnable);
+        postDelayed(mSlidingRunnable, ANIMATION_FRAME_DURATION);
         stopTracking();
     }
 
@@ -569,7 +567,7 @@
                     (mVertical ? getHeight() - mHandleHeight : getWidth() - mHandleWidth);
             moveHandle((int) mAnimationPosition);
             mAnimating = true;
-            mHandler.removeMessages(MSG_ANIMATE);
+            removeCallbacks(mSlidingRunnable);
             long now = SystemClock.uptimeMillis();
             mAnimationLastTime = now;
             mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;
@@ -577,7 +575,7 @@
         } else {
             if (mAnimating) {
                 mAnimating = false;
-                mHandler.removeMessages(MSG_ANIMATE);
+                removeCallbacks(mSlidingRunnable);
             }
             moveHandle(position);
         }
@@ -709,8 +707,7 @@
             } else {
                 moveHandle((int) mAnimationPosition);
                 mCurrentAnimationTime += ANIMATION_FRAME_DURATION;
-                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE),
-                        mCurrentAnimationTime);
+                postDelayed(mSlidingRunnable, ANIMATION_FRAME_DURATION);
             }
         }
     }
@@ -974,13 +971,10 @@
         }
     }
 
-    private class SlidingHandler extends Handler {
-        public void handleMessage(Message m) {
-            switch (m.what) {
-                case MSG_ANIMATE:
-                    doAnimation();
-                    break;
-            }
+    private final Runnable mSlidingRunnable = new Runnable() {
+        @Override
+        public void run() {
+            doAnimation();
         }
-    }
+    };
 }
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index bcde315..ff10287 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -137,7 +137,13 @@
 
     private boolean mShowCurrentUserTime;
 
-    private final ContentObserver mFormatChangeObserver = new ContentObserver(new Handler()) {
+    private ContentObserver mFormatChangeObserver;
+    private class FormatChangeObserver extends ContentObserver {
+
+        public FormatChangeObserver(Handler handler) {
+            super(handler);
+        }
+
         @Override
         public void onChange(boolean selfChange) {
             chooseFormat();
@@ -553,13 +559,18 @@
     }
 
     private void registerObserver() {
-        final ContentResolver resolver = getContext().getContentResolver();
-        if (mShowCurrentUserTime) {
-            resolver.registerContentObserver(Settings.System.CONTENT_URI, true,
-                    mFormatChangeObserver, UserHandle.USER_ALL);
-        } else {
-            resolver.registerContentObserver(Settings.System.CONTENT_URI, true,
-                    mFormatChangeObserver);
+        if (isAttachedToWindow()) {
+            if (mFormatChangeObserver == null) {
+                mFormatChangeObserver = new FormatChangeObserver(getHandler());
+            }
+            final ContentResolver resolver = getContext().getContentResolver();
+            if (mShowCurrentUserTime) {
+                resolver.registerContentObserver(Settings.System.CONTENT_URI, true,
+                        mFormatChangeObserver, UserHandle.USER_ALL);
+            } else {
+                resolver.registerContentObserver(Settings.System.CONTENT_URI, true,
+                        mFormatChangeObserver);
+            }
         }
     }
 
@@ -568,8 +579,10 @@
     }
 
     private void unregisterObserver() {
-        final ContentResolver resolver = getContext().getContentResolver();
-        resolver.unregisterContentObserver(mFormatChangeObserver);
+        if (mFormatChangeObserver != null) {
+            final ContentResolver resolver = getContext().getContentResolver();
+            resolver.unregisterContentObserver(mFormatChangeObserver);
+        }
     }
 
     private void onTimeChanged() {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1aeb9c93..13b1c4b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -16,6 +16,8 @@
 
 package android.widget;
 
+import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
+
 import android.R;
 import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
@@ -118,14 +120,14 @@
 import android.view.MotionEvent;
 import android.view.PointerIcon;
 import android.view.View;
-import android.view.ViewParent;
-import android.view.ViewStructure;
 import android.view.ViewConfiguration;
 import android.view.ViewDebug;
 import android.view.ViewGroup.LayoutParams;
-import android.view.ViewRootImpl;
-import android.view.ViewTreeObserver;
 import android.view.ViewHierarchyEncoder;
+import android.view.ViewParent;
+import android.view.ViewRootImpl;
+import android.view.ViewStructure;
+import android.view.ViewTreeObserver;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -153,8 +155,6 @@
 import java.util.ArrayList;
 import java.util.Locale;
 
-import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
-
 /**
  * Displays text to the user and optionally allows them to edit it.  A TextView
  * is a complete text editor, however the basic class is configured to not
@@ -1478,7 +1478,12 @@
                         }
                     }
                 }
+            } else if (mText instanceof Spannable) {
+                // Reset the selection.
+                stopTextActionMode();
+                Selection.setSelection((Spannable) mText, getSelectionStart(), getSelectionEnd());
             }
+
             if (mEditor.hasSelectionController()) {
                 mEditor.startSelectionActionMode();
             }
@@ -5282,14 +5287,6 @@
             mEditor.mCreatedWithASelection = false;
         }
 
-        // Phone specific code (there is no ExtractEditText on tablets).
-        // ExtractEditText does not call onFocus when it is displayed, and mHasSelectionOnFocus can
-        // not be set. Do the test here instead.
-        if (isInExtractedMode() && hasSelection() && mEditor != null
-                && mEditor.mTextActionMode == null && isShown() && hasWindowFocus()) {
-            mEditor.startSelectionActionMode();
-        }
-
         unregisterForPreDraw();
 
         return true;
diff --git a/core/java/android/widget/ViewFlipper.java b/core/java/android/widget/ViewFlipper.java
index 94e7ba1..65af7aa 100644
--- a/core/java/android/widget/ViewFlipper.java
+++ b/core/java/android/widget/ViewFlipper.java
@@ -96,7 +96,7 @@
         // home screen. Therefore, we register the receiver as the current
         // user not the one the context is for.
         getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
-                filter, null, mHandler);
+                filter, null, getHandler());
 
         if (mAutoStart) {
             // Automatically start when requested
@@ -173,10 +173,9 @@
         if (running != mRunning) {
             if (running) {
                 showOnly(mWhichChild, flipNow);
-                Message msg = mHandler.obtainMessage(FLIP_MSG);
-                mHandler.sendMessageDelayed(msg, mFlipInterval);
+                postDelayed(mFlipRunnable, mFlipInterval);
             } else {
-                mHandler.removeMessages(FLIP_MSG);
+                removeCallbacks(mFlipRunnable);
             }
             mRunning = running;
         }
@@ -209,17 +208,12 @@
         return mAutoStart;
     }
 
-    private final int FLIP_MSG = 1;
-
-    private final Handler mHandler = new Handler() {
+    private final Runnable mFlipRunnable = new Runnable() {
         @Override
-        public void handleMessage(Message msg) {
-            if (msg.what == FLIP_MSG) {
-                if (mRunning) {
-                    showNext();
-                    msg = obtainMessage(FLIP_MSG);
-                    sendMessageDelayed(msg, mFlipInterval);
-                }
+        public void run() {
+            if (mRunning) {
+                showNext();
+                postDelayed(mFlipRunnable, mFlipInterval);
             }
         }
     };
diff --git a/core/java/android/widget/ZoomButton.java b/core/java/android/widget/ZoomButton.java
index 0255bdb..7d4f8ed 100644
--- a/core/java/android/widget/ZoomButton.java
+++ b/core/java/android/widget/ZoomButton.java
@@ -17,7 +17,6 @@
 package android.widget;
 
 import android.content.Context;
-import android.os.Handler;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -26,12 +25,11 @@
 
 public class ZoomButton extends ImageButton implements OnLongClickListener {
 
-    private final Handler mHandler;
     private final Runnable mRunnable = new Runnable() {
         public void run() {
             if (hasOnClickListeners() && mIsInLongpress && isEnabled()) {
                 callOnClick();
-                mHandler.postDelayed(this, mZoomSpeed);
+                postDelayed(this, mZoomSpeed);
             }
         }
     };
@@ -53,7 +51,6 @@
 
     public ZoomButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        mHandler = new Handler();
         setOnLongClickListener(this);
     }
 
@@ -72,7 +69,7 @@
 
     public boolean onLongClick(View v) {
         mIsInLongpress = true;
-        mHandler.post(mRunnable);
+        post(mRunnable);
         return true;
     }
         
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 70d5f62..b7b7400 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -373,6 +373,11 @@
         int targetsToQuery = 0;
         for (int i = 0, N = adapter.getDisplayResolveInfoCount(); i < N; i++) {
             final DisplayResolveInfo dri = adapter.getDisplayResolveInfo(i);
+            if (adapter.getScore(dri) == 0) {
+                // A score of 0 means the app hasn't been used in some time;
+                // don't query it as it's not likely to be relevant.
+                continue;
+            }
             final ActivityInfo ai = dri.getResolveInfo().activityInfo;
             final Bundle md = ai.metaData;
             final String serviceName = md != null ? convertServiceName(ai.packageName,
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index a14afa0..41aa9ca 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -114,7 +114,7 @@
     return parcel ? parcel->dataCapacity() : 0;
 }
 
-static void android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nativePtr, jint size)
+static jlong android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nativePtr, jint size)
 {
     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
     if (parcel != NULL) {
@@ -122,7 +122,9 @@
         if (err != NO_ERROR) {
             signalExceptionForError(env, clazz, err);
         }
+        return parcel->getOpenAshmemSize();
     }
+    return 0;
 }
 
 static void android_os_Parcel_setDataPosition(JNIEnv* env, jclass clazz, jlong nativePtr, jint pos)
@@ -304,7 +306,7 @@
     }
 }
 
-static void android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
+static jlong android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
 {
     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
     if (parcel != NULL) {
@@ -313,7 +315,9 @@
         if (err != NO_ERROR) {
             signalExceptionForError(env, clazz, err);
         }
+        return parcel->getOpenAshmemSize();
     }
+    return 0;
 }
 
 static jbyteArray android_os_Parcel_createByteArray(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -546,12 +550,14 @@
     return reinterpret_cast<jlong>(parcel);
 }
 
-static void android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr)
+static jlong android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr)
 {
     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
     if (parcel != NULL) {
         parcel->freeData();
+        return parcel->getOpenAshmemSize();
     }
+    return 0;
 }
 
 static void android_os_Parcel_destroy(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -589,12 +595,12 @@
     return ret;
 }
 
-static void android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr,
-                                         jbyteArray data, jint offset, jint length)
+static jlong android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr,
+                                          jbyteArray data, jint offset, jint length)
 {
     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
     if (parcel == NULL || length < 0) {
-       return;
+       return 0;
     }
 
     jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(data, 0);
@@ -608,24 +614,26 @@
 
         env->ReleasePrimitiveArrayCritical(data, array, 0);
     }
+    return parcel->getOpenAshmemSize();
 }
 
-static void android_os_Parcel_appendFrom(JNIEnv* env, jclass clazz, jlong thisNativePtr,
-                                         jlong otherNativePtr, jint offset, jint length)
+static jlong android_os_Parcel_appendFrom(JNIEnv* env, jclass clazz, jlong thisNativePtr,
+                                          jlong otherNativePtr, jint offset, jint length)
 {
     Parcel* thisParcel = reinterpret_cast<Parcel*>(thisNativePtr);
     if (thisParcel == NULL) {
-       return;
+       return 0;
     }
     Parcel* otherParcel = reinterpret_cast<Parcel*>(otherNativePtr);
     if (otherParcel == NULL) {
-       return;
+       return thisParcel->getOpenAshmemSize();
     }
 
     status_t err = thisParcel->appendFrom(otherParcel, offset, length);
     if (err != NO_ERROR) {
         signalExceptionForError(env, clazz, err);
     }
+    return thisParcel->getOpenAshmemSize();
 }
 
 static jboolean android_os_Parcel_hasFileDescriptors(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -718,7 +726,7 @@
     {"nativeDataAvail",           "(J)I", (void*)android_os_Parcel_dataAvail},
     {"nativeDataPosition",        "(J)I", (void*)android_os_Parcel_dataPosition},
     {"nativeDataCapacity",        "(J)I", (void*)android_os_Parcel_dataCapacity},
-    {"nativeSetDataSize",         "(JI)V", (void*)android_os_Parcel_setDataSize},
+    {"nativeSetDataSize",         "(JI)J", (void*)android_os_Parcel_setDataSize},
     {"nativeSetDataPosition",     "(JI)V", (void*)android_os_Parcel_setDataPosition},
     {"nativeSetDataCapacity",     "(JI)V", (void*)android_os_Parcel_setDataCapacity},
 
@@ -733,7 +741,7 @@
     {"nativeWriteDouble",         "(JD)V", (void*)android_os_Parcel_writeDouble},
     {"nativeWriteString",         "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString},
     {"nativeWriteStrongBinder",   "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder},
-    {"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)V", (void*)android_os_Parcel_writeFileDescriptor},
+    {"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)J", (void*)android_os_Parcel_writeFileDescriptor},
 
     {"nativeCreateByteArray",     "(J)[B", (void*)android_os_Parcel_createByteArray},
     {"nativeReadBlob",            "(J)[B", (void*)android_os_Parcel_readBlob},
@@ -751,12 +759,12 @@
     {"clearFileDescriptor",       "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_clearFileDescriptor},
 
     {"nativeCreate",              "()J", (void*)android_os_Parcel_create},
-    {"nativeFreeBuffer",          "(J)V", (void*)android_os_Parcel_freeBuffer},
+    {"nativeFreeBuffer",          "(J)J", (void*)android_os_Parcel_freeBuffer},
     {"nativeDestroy",             "(J)V", (void*)android_os_Parcel_destroy},
 
     {"nativeMarshall",            "(J)[B", (void*)android_os_Parcel_marshall},
-    {"nativeUnmarshall",          "(J[BII)V", (void*)android_os_Parcel_unmarshall},
-    {"nativeAppendFrom",          "(JJII)V", (void*)android_os_Parcel_appendFrom},
+    {"nativeUnmarshall",          "(J[BII)J", (void*)android_os_Parcel_unmarshall},
+    {"nativeAppendFrom",          "(JJII)J", (void*)android_os_Parcel_appendFrom},
     {"nativeHasFileDescriptors",  "(J)Z", (void*)android_os_Parcel_hasFileDescriptors},
     {"nativeWriteInterfaceToken", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeInterfaceToken},
     {"nativeEnforceInterface",    "(JLjava/lang/String;)V", (void*)android_os_Parcel_enforceInterface},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 148e7c1..f300741 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1715,12 +1715,12 @@
         android:protectionLevel="signature|privileged" />
 
     <!-- Allows applications to change network connectivity state.
-         <p>Protection level: signature
+         <p>Protection level: normal
     -->
     <permission android:name="android.permission.CHANGE_NETWORK_STATE"
         android:description="@string/permdesc_changeNetworkState"
         android:label="@string/permlab_changeNetworkState"
-        android:protectionLevel="signature|preinstalled|appop|pre23" />
+        android:protectionLevel="normal" />
 
     <!-- Allows an application to clear the caches of all installed
          applications on the device.
@@ -2075,10 +2075,11 @@
     <permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
         android:protectionLevel="signature" />
 
-    <!-- Allows an application to monitor changes in tablet mode.
+    <!-- Allows an application to query tablet mode state and monitor changes
+         in it.
          <p>Not for use by third-party applications.
          @hide -->
-    <permission android:name="android.permission.TABLET_MODE_LISTENER"
+    <permission android:name="android.permission.TABLET_MODE"
         android:protectionLevel="signature" />
 
     <!-- Allows an application to request installing packages. Apps
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index a58ec89..e7acf7e 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Verkeerde PIN-kode."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Om te ontsluit, druk Kieslys dan 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Noodnommer"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Geen diens nie."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Geen diens nie"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skerm gesluit."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Druk kieslys om oop te sluit of maak noodoproep."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Druk kieslys om oop te maak."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 80665a27..5bd9ccf 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ትክክል ያልሆነ ፒን  ኮድ።"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"ለመክፈት፣ምናሌ ተጫን ከዛ 0"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"የአደጋ ጊዜቁጥር"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"ከአገልግሎት መስጫ ክልል ውጪ"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"ምንም አገልግሎት የለም"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"ማሳያ መቆለፊያ።"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ለመክፈት ምናሌ ተጫንወይም የአደጋ ጊዜ ጥሪ አድርግ።"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"ለመክፈት ምናሌ ተጫን"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 22eaabe..4dd5144 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -654,7 +654,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"‏رمز PIN غير صحيح."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"لإلغاء التأمين، اضغط على \"القائمة\" ثم على 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"رقم الطوارئ"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"لا تتوفر خدمة"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"لا خدمة"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"الشاشة مؤمّنة."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"اضغط على \"القائمة\" لإلغاء التأمين أو إجراء اتصال بالطوارئ."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"اضغط على \"القائمة\" لإلغاء التأمين."</string>
diff --git a/core/res/res/values-az-rAZ/strings.xml b/core/res/res/values-az-rAZ/strings.xml
index 401e5b8..c828fc4 100644
--- a/core/res/res/values-az-rAZ/strings.xml
+++ b/core/res/res/values-az-rAZ/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Yanlış PIN kodu."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Kilidi açmaq üçün Menyu, sonra 0 basın."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Təcili nömrə"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Xidmət yoxdur."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Xidmət yoxdur"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekran kilidlənib."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Təcili zəng kilidini açmaq və ya yerləşdirmək üçün Menyu düyməsinə basın."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Kilidi açmaq üçün Menyu düyməsinə basın."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 6c3fa54..e474e7e 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Неправилен ПИН код."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"За да отключите, натиснете „Меню“ и после 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Спешен номер"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Няма покритие."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Няма покритие"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Екранът е заключен."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Натиснете „Меню“, за да отключите или да извършите спешно обаждане."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Натиснете „Меню“, за да отключите."</string>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index 3f986a0..1f365b9 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ভুল পিন কোড৷"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"আনলক করতে, মেনু টিপুন তারপর ০ টিপুন৷"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"জরুরী নম্বর"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"কোনো পরিষেবা নেই৷"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"কোনো পরিষেবা নেই"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"স্ক্রীণ লক করা আছে৷"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"আনলক করতে বা জরুরী কল করতে মেনু টিপুন৷"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"আনলক করতে মেনু টিপুন৷"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 8d004df..046ca14 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Codi PIN incorrecte."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Per desbloquejar-lo, premeu Menú i després 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número d\'emergència"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Sense servei."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sense servei"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Pantalla bloquejada."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Premeu Menú per desbloquejar-lo o per fer una trucada d\'emergència."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Premeu Menú per desbloquejar."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 6ead72f..8f3976d 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -652,7 +652,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Nesprávný kód PIN."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Chcete-li telefon odemknout, stiskněte Menu a poté 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Číslo tísňové linky"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Žádný signál"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Žádný signál"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Obrazovka uzamčena."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Chcete-li odemknout telefon nebo provést tísňové volání, stiskněte Menu."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Telefon odemknete stisknutím tlačítka Menu."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 346cdb1..82b550b 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -219,7 +219,7 @@
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Flytilstand er slået FRA"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Indstillinger"</string>
     <string name="global_action_assist" msgid="3892832961594295030">"Assistance"</string>
-    <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
+    <string name="global_action_voice_assist" msgid="7751191495200504480">"Taleassistent"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Lås nu"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
     <string name="safeMode" msgid="2788228061547930246">"Sikker tilstand"</string>
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Forkert pinkode."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Tryk på Menu og dernæst på 0 for at låse op."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nødnummer"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ingen dækning."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ingen dækning"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skærmen er låst."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Tryk på Menu for at låse op eller foretage et nødopkald."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Tryk på Menu for at låse op."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 4e8b455..9ae5473b 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Falscher PIN-Code"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Drücken Sie zum Entsperren die Menütaste und dann auf \"0\"."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Notrufnummer"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Kein Dienst"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Kein Dienst"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Display gesperrt"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Drücken Sie die Menütaste, um das Telefon zu entsperren oder einen Notruf zu tätigen."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Zum Entsperren die Menütaste drücken"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 3f5e1af..56deed13 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Λανθασμένος κωδικός PIN."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Για ξεκλείδωμα, πατήστε το πλήκτρο Menu και, στη συνέχεια, το πλήκτρο 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Αριθμός έκτακτης ανάγκης"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Καμία υπηρεσία."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Δίκτυο μη διαθέσιμο"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Η οθόνη κλειδώθηκε."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Πατήστε \"Menu\" για ξεκλείδωμα ή για κλήση έκτακτης ανάγκης."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Πατήστε \"Μενού\" για ξεκλείδωμα."</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 22179ed..53adc2b 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Incorrect PIN code."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"To unlock, press Menu, then 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Emergency number"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"No service"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"No service"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Screen locked."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Press Menu to unlock or place emergency call."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Press Menu to unlock."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 22179ed..53adc2b 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Incorrect PIN code."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"To unlock, press Menu, then 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Emergency number"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"No service"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"No service"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Screen locked."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Press Menu to unlock or place emergency call."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Press Menu to unlock."</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 22179ed..53adc2b 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Incorrect PIN code."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"To unlock, press Menu, then 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Emergency number"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"No service"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"No service"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Screen locked."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Press Menu to unlock or place emergency call."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Press Menu to unlock."</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 08f513b..25ba6db8 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorrecto"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, presiona el menú y luego 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergencia"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Sin servicio"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sin servicio"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Pantalla bloqueada."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Presiona el Menú para desbloquear o realizar una llamada de emergencia."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Presionar Menú para desbloquear."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 4d1500d..e3a4b66 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorrecto"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear el teléfono, pulsa la tecla de menú y, a continuación, pulsa 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergencia"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Sin servicio"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sin servicio"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Pantalla bloqueada"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Pulsa la tecla de menú para desbloquear el teléfono o realizar una llamada de emergencia."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Pulsa la tecla de menú para desbloquear la pantalla."</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 09454d6..41474a4 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Vale PIN-kood."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Avamiseks vajutage menüüklahvi, seejärel klahvi 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Hädaabinumber"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Teenus puudub."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Teenus puudub"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekraan lukus."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Vajutage avamiseks või hädaabikõne tegemiseks menüünuppu"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Vajutage avamiseks menüüklahvi."</string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index 0815aeb..3a23fba 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN kode okerra."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Desblokeatzeko, sakatu Menua eta, ondoren, 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Larrialdietarako zenbakia"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ez dago zerbitzurik."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ez dago zerbitzurik"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Pantaila blokeatuta dago."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Desblokeatzeko edo larrialdi-deia egiteko, sakatu Menua."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Desblokeatzeko, sakatu Menua."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 683afb4..ae6598b 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"پین کد اشتباه است."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"برای بازگشایی قفل، منو را فشار دهید و سپس 0 را فشار دهید."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"شماره اضطراری"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"سرویسی وجود ندارد."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"بدون سرویس"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"صفحه قفل شد."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"برای بازگشایی قفل یا انجام تماس اضطراری روی منو فشار دهید."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"برای بازگشایی قفل روی منو فشار دهید."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index f8fb76f7..d8778a8 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN-koodi väärin."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Poista lukitus painamalla Valikko-painiketta ja 0-näppäintä."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Hätänumero"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ei yhteyttä."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ei yhteyttä"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Näyttö lukittu."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Poista lukitus tai soita hätäpuhelu painamalla Valikko-painiketta."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Poista lukitus painamalla Valikko-painiketta."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 286459a..6f59ee0 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"NIP erroné."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Pour déverrouiller le téléphone, appuyez sur \"Menu\", puis sur 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Numéro d\'urgence"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Aucun service"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Aucun service"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Écran verrouillé"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Appuyez sur \"Menu\" pour débloquer le téléphone ou appeler un numéro d\'urgence."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Appuyez sur \"Menu\" pour déverrouiller l\'appareil."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index e4daeb1..9f08389 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Le code PIN est erroné."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Pour déverrouiller le clavier, appuyez sur \"Menu\" puis sur 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Numéro d\'urgence"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Aucun service"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Aucun service"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Écran verrouillé"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Appuyez sur \"Menu\" pour déverrouiller le téléphone ou appeler un numéro d\'urgence"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Appuyez sur \"Menu\" pour déverrouiller le téléphone."</string>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index 2abd2be..21e6ee7 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorrecto"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, preme Menú e, a continuación, 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emerxencia"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Non hai servizo."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sen servizo"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Pantalla bloqueada"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Preme Menú para desbloquear ou realizar unha chamada de emerxencia."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Preme Menú para desbloquear."</string>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index 8f46194..6dc0371 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ખોટો PIN કોડ."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"અનલૉક કરવા માટે, મેનૂ દબાવો તે પછી 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"ઇમરજન્સિ નંબર"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"કોઈ સેવા નથી."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"કોઈ સેવા નથી"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"સ્ક્રીન લૉક કરી."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"અનલૉક કરવા માટે અથવા કટોકટીનો કૉલ કરવા માટે મેનૂ દબાવો."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"અનલૉક કરવા માટે મેનૂ દબાવો."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 61b21bd..6b1a91f 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"गलत पिन कोड."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"अनलॉक करने के लिए, मेनू दबाएं और फिर 0 दबाएं."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"आपातकालीन नंबर"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"कोई सेवा नहीं."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"कोई सेवा नहीं"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"स्‍क्रीन लॉक की गई है."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"अनलॉक करने के लिए मेनू दबाएं या आपातलकालीन कॉल करें."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"अनलॉक करने के लिए मेनू दबाएं."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 680dabd..67d7110 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -651,7 +651,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Netočan PIN kôd."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Za otključavanje pritisnite Izbornik pa 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Broj hitne službe"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nema usluge."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Nema usluge"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Zaslon zaključan."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Pritisnite Izbornik za otključavanje ili pozivanje hitnih službi."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Pritisnite Izbornik za otključavanje."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index faed9b9..7e1f55b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Helytelen PIN kód."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"A feloldáshoz nyomja meg a Menü, majd a 0 gombot."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Segélyhívó szám"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nincs szolgáltatás."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Nincs szolgáltatás"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"A képernyő le van zárva."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"A feloldáshoz vagy segélyhívás kezdeményezéséhez nyomja meg a Menü gombot."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"A feloldáshoz nyomja meg a Menü gombot."</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 9a0e603..9259c06 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Սխալ PIN ծածկագիր:"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Ապակողպման համար սեղմեք Ցանկ, ապա 0:"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Արտակարգ իրավիճակների հեռախոսահամար"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ծառայություն չկա:"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ծառայություն չկա"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Էկրանը կողպված է:"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Սեղմեք Ցանկ` ապակողպելու համար, կամ կատարեք արտակարգ իրավիճակների զանգ:"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Ապակողպելու համար սեղմեք Ցանկը:"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 1bbd020..0f6cd3e 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Kode PIN salah."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Untuk membuka, tekan Menu lalu 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nomor darurat"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Tidak ada layanan."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Tidak ada layanan"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Layar terkunci."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Tekan Menu untuk membuka atau melakukan panggilan darurat."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Tekan Menu untuk membuka."</string>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index 2a97573..29f7408 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Rangt PIN-númer."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Til að taka úr lás ýtirðu á valmyndartakkann og síðan 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Neyðarnúmer"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ekkert símasamband."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ekkert símasamband"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skjár læstur."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Ýttu á valmyndartakkann til að taka úr lás eða hringja neyðarsímtal."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Ýttu á valmyndartakkann til að taka úr lás."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index c56b41b..ab541ea 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Codice PIN errato."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Per sbloccare, premi Menu, poi 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Numero di emergenza"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nessun servizio."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Nessun servizio"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Schermo bloccato."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Premi Menu per sbloccare o effettuare chiamate di emergenza."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Premi Menu per sbloccare."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 26fe698..de19821 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -652,7 +652,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"‏קוד PIN שגוי"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"כדי לבטל את הנעילה, לחץ על \'תפריט\' ולאחר מכן על 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"מספר חירום"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"אין שירות"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"אין שירות"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"המסך נעול."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"לחץ על \'תפריט\' כדי לבטל את הנעילה או כדי לבצע שיחת חירום."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"לחץ על \'תפריט\' כדי לבטל את הנעילה."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 6b1c833..45e82ac 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PINコードが正しくありません。"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"MENU、0キーでロック解除"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"緊急通報番号"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"通信サービスはありません"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"通信サービスはありません"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"画面ロック中"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"MENUキーでロック解除(または緊急通報)"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"MENUキーでロック解除"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index e1221c4..7900f83 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"არასწორი PIN კოდი."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"განბლოკვისათვის დააჭირეთ მენიუს და შემდეგ 0-ს."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"გადაუდებელი დახმარების ნომრები"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"სერვისი არ არის."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"სერვისი არ არის"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"ეკრანი დაბლოკილია."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"განბლოკვისთვის ან გადაუდებელი ზარისთვის დააჭირეთ მენიუს."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"განბლოკვისთვის დააჭირეთ მენიუს."</string>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index b3172ff..23ec612 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Қате PIN код"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Бекітпесін ашу үшін Мәзір, одан кейін 0 пернесін түртіңіз."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Төтенше жағдай нөмірі"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Қызмет көрсетілмейді."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Қызмет жоқ"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Экран бекітілген."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Бекітпесін ашу үшін немесе төтенше қоңырауды табу үшін Мәзір тармағын басыңыз."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Ашу үшін Мәзір пернесін басыңыз."</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 822a303..8ffa8b8 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"កូដ PIN មិន​ត្រឹមត្រូវ។"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"ដើម្បី​ដោះ​សោ​​ ចុច​ម៉ឺនុយ​ បន្ទាប់មក 0 ។"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"លេខ​ពេល​អាសន្ន"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"គ្មាន​សេវា"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"គ្មានសេវាទេ"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"ចាក់​អេក្រង់។"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ចុច​ម៉ឺនុយ ដើម្បី​ដោះ​សោ​ ឬ​ហៅ​ពេល​អាសន្ន។"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"ចុច​ម៉ឺនុយ ដើម្បី​ដោះ​សោ។"</string>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index 73d7ffc..a840c7e 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ತಪ್ಪಾದ ಪಿನ್‌ ಕೋಡ್."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು, ಮೆನು ನಂತರ 0 ಒತ್ತಿರಿ."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"ತುರ್ತು ಸಂಖ್ಯೆ"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"ಸೇವೆ ಇಲ್ಲ."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"ಯಾವುದೇ ಸೇವೆಯಿಲ್ಲ"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"ಪರದೆ ಲಾಕ್ ಆಗಿದೆ."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ ಇಲ್ಲವೇ ತುರ್ತು ಕರೆಯನ್ನು ಮಾಡಿ."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 0d76af2..59b0214 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN 코드가 잘못되었습니다."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"잠금해제하려면 메뉴를 누른 다음 0을 누릅니다."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"비상 전화번호"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"서비스 불가"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"서비스 불가"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"화면 잠김"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"비상 전화를 걸거나 잠금해제하려면 메뉴를 누르세요."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"잠금해제하려면 메뉴를 누르세요."</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 22399b0..59c34dc 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN-код туура эмес."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Кулпусун ачуу үчүн, Менюна андан соң 0 баскычын басыңыз."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Шашылыш чалуу номери"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Байланыш жок."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Байланыш жок"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Экран кулпуланды."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Кулпусун ачып же Шашылыш чалуу аткаруу үчүн менюну басыңыз."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Бөгөттөн чыгаруу үчүн Менюну басыңыз."</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index 1d39230..f131914 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ລະຫັດ PIN ບໍ່ຖືກຕ້ອງ."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"ເພື່ອປົດລັອກ, ໃຫ້ກົດເມນູ ແລ້ວກົດ 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"ເບີໂທສຸກເສີນ"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"ບໍ່ມີບໍລິການ."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"ບໍ່ມີບໍລິການ"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"ລັອກໜ້າຈໍແລ້ວ."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ກົດ ເມນູ ເພື່ອປົດລັອກ ຫຼື ໂທອອກຫາເບີສຸກເສີນ."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"ກົດ \"ເມນູ\" ເພື່ອປົດລັອກ."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index dbb8fbb..536a107 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -652,7 +652,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Neteisingas PIN kodas."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Jei norite atrakinti, paspauskite „Meniu“ ir 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Pagalbos numeris"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nėra paslaugos."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Nėra paslaugos"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekranas užrakintas."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Paspauskite „Meniu“, kad atrakintumėte ar skambintumėte pagalbos numeriu."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Paspauskite „Meniu“, jei norite atrakinti."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index eaf7b46..288493a 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -651,7 +651,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN kods nav pareizs."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Lai atbloķētu, nospiediet Izvēlne, pēc tam 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Ārkārtas numurs"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nav pakalpojuma."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Nav pakalpojuma"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekrāns ir bloķēts."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Nospiediet Izvēlne, lai atbloķētu, vai veiciet ārkārtas zvanu."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Lai atbloķētu, nospiediet vienumu Izvēlne."</string>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index 17e0d49..eab72bf 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Погрешен ПИН код."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"За да го отклучите, притиснете „Мени“ и потоа „0“."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Број за итни случаи"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Нема услуга."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Нема услуга"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Екранот е заклучен."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Притисни „Мени“ да се отклучи или да направи итен повик."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Притиснете „Мени“ за да се отклучи."</string>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index 3df3c86..caaedda 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"പിൻ കോഡ് തെറ്റാണ്."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"അൺലോക്ക് ചെയ്യുന്നതിന് മെനു, 0 എന്നിവ അമർത്തുക."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"അടിയന്തര നമ്പർ"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"സേവനമില്ല"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"സേവനമില്ല"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"സ്‌ക്രീൻ ലോക്കുചെയ്‌തു."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"അൺലോക്ക് ചെയ്യുന്നതിനായി മെനു അമർത്തുക അല്ലെങ്കിൽ അടിയന്തര കോൾ വിളിക്കുക."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"അൺലോക്കുചെയ്യാൻ മെനു അമർത്തുക."</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index ff8cc4d..c02760d 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Буруу PIN код."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Тайлах бол Цэсийг дараад 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Яаралтай дугаар"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Үйлчилгээ байхгүй."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Үйлчилгээ байхгүй"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Дэлгэц түгжигдсэн."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Яаралтай дуудлага хийх буюу эсвэл түгжээг тайлах бол цэсийг дарна уу."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Тайлах бол цэсийг дарна уу."</string>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index 45bc3c5..d557661 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"अयोग्य पिन कोड."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"अनलॉक करण्यासाठी, मेनू दाबा नंतर 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"आणीबाणीचा नंबर"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"सेवा नाही."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"सेवा नाही"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"स्क्रीन लॉक केली."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"अनलॉक करण्‍यासाठी मेनू दाबा किंवा आणीबाणीचा कॉल करा."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"अनलॉक करण्यासाठी मेनू दाबा."</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index 1508d0e..8e5113b 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Kod PIN salah."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Untuk membuka kunci, tekan Menu, kemudian 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nombor kecemasan"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Tiada perkhidmatan."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Tiada perkhidmatan"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skrin dikunci."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Tekan Menu untuk menyahsekat atau membuat panggilan kecemasan."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Tekan Menu untuk membuka kunci."</string>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index a09093f..427e844 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ပင်နံပါတ်မှားနေပါသည်"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"သော့ဖွင့်ရန် Menu ထိုနောက်0ကိုနှိပ်ပါ"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"အရေးပေါ်နံပါတ်"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"ဆားဗစ် မရှိပါ"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"ဝန်ဆောင်မှု မရှိပါ"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"မျက်နှာပြင်အားသော့ချထားသည်"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ဖွင့်ရန်သို့မဟုတ်အရေးပေါ်ခေါ်ဆိုခြင်းပြုလုပ်ရန် မီနူးကိုနှိပ်ပါ"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"မီးနူးကို နှိပ်ခြင်းဖြင့် သော့ဖွင့်ပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index c2d2b80..e231e2a 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Feil personlig kode."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"For å låse opp, trykk på menyknappen og deretter 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nødnummer"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ingen tjeneste."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ingen dekning"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skjermen er låst"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Trykk på menyknappen for å låse opp eller ringe et nødnummer."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Trykk på menyknappen for å låse opp."</string>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index e4b6c22..8181f6c 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"गलत PIN कोड।"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"अनलक गर्न मेनु थिच्नुहोस् र त्यसपछि ० थिच्नुहोस्।"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"आपतकालीन नम्बर"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"सेवा छैन।"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"कुनै सेवा छैन"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"स्क्रिन लक गरिएको।"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"अनलक वा आपतकालीन कल गर्न मेनु थिच्नुहोस्।"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"अनलक गर्न मेनु थिच्नुहोस्।"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index b208f48..361a816 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Onjuiste pincode."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Druk op \'Menu\' en vervolgens op 0 om te ontgrendelen."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Alarmnummer"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Geen service"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Geen service"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Scherm vergrendeld."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Druk op \'Menu\' om te ontgrendelen of noodoproep te plaatsen."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Druk op \'Menu\' om te ontgrendelen."</string>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index 8272d71..0b80c4a 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ਗ਼ਲਤ PIN ਕੋਡ।"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"ਅਨਲੌਕ ਕਰਨ ਲਈ, ਪਹਿਲਾਂ ਮੀਨੂ ਫਿਰ 0 ਦਬਾਓ।"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"ਐਮਰਜੈਂਸੀ ਨੰਬਰ"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"ਕੋਈ ਸੇਵਾ ਨਹੀਂ।"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"ਕੋਈ ਸੇਵਾ ਨਹੀਂ"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"ਸਕ੍ਰੀਨ ਲੌਕ ਕੀਤੀ।"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ਅਨਲੌਕ ਕਰਨ ਲਈ ਮੀਨੂ ਦਬਾਓ ਜਾਂ ਐਮਰਜੈਂਸੀ ਕਾਲ ਕਰੋ।"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"ਅਨਲੌਕ ਕਰਨ ਲਈ ਮੀਨੂ ਦਬਾਓ।"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 34d26dc..d72281b 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -652,7 +652,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Błędny kod PIN"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Aby odblokować, naciśnij Menu, a następnie 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Numer alarmowy"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Brak usługi"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Brak usługi"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekran zablokowany."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Naciśnij Menu, aby odblokować lub wykonać połączenie alarmowe."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Naciśnij Menu, aby odblokować."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index bd1b2f0..daf17ef 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorreto."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, pressione Menu e, em seguida, 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergência"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Sem serviço."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sem serviço"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Tela bloqueada."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Pressione Menu para desbloquear ou fazer uma chamada de emergência."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Pressione Menu para desbloquear."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 897dcd6..b98a1cd 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorreto."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, prima Menu e, em seguida, 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergência"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nenhum serviço"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sem rede móvel"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ecrã bloqueado."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Prima Menu para desbloquear ou efectuar uma chamada de emergência."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Prima Menu para desbloquear."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index bd1b2f0..daf17ef 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorreto."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, pressione Menu e, em seguida, 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergência"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Sem serviço."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sem serviço"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Tela bloqueada."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Pressione Menu para desbloquear ou fazer uma chamada de emergência."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Pressione Menu para desbloquear."</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 428e139..6c576ad 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -651,7 +651,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Cod PIN incorect."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Pentru a debloca, apăsaţi Meniu, apoi 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Număr de urgență"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Fără serviciu."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Fără semnal"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ecranul este blocat."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Apăsați Meniu pentru a debloca sau pentru a efectua apeluri de urgență."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Apăsaţi Meniu pentru deblocare."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 525e088..15cbe4a 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -652,7 +652,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Неверный PIN-код."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Для разблокировки нажмите \"Меню\", а затем 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Экстренная служба"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Нет сигнала"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Нет сигнала"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Экран заблокирован."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Нажмите \"Меню\", чтобы разблокировать экран или вызвать службу экстренной помощи."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Для разблокировки нажмите \"Меню\"."</string>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index 0135884..5e6cfcd 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"වැරදි PIN කේතයකි."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"අගුළු ඇරීමට, මෙනුව ඔබා පසුව 0 ද ඔබන්න."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"හදිසි ඇමතුම් අංකය"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"සේවාව නැත."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"සේවාව නැත"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"තිරය අගුළු දමා ඇත."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"අගුළු හැරීමට මෙනුව ඔබන්න හෝ හදිසි ඇමතුම ලබාගන්න."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"අගුළු හැරීමට මෙනු ඔබන්න."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 5bd6867..dd3cbca 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -652,7 +652,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Nesprávny kód PIN."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Ak chcete telefón odomknúť, stlačte Menu a následne 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Číslo tiesňového volania"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Žiadny signál"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Žiadny signál"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Obrazovka je uzamknutá."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Ak chcete odomknúť telefón alebo uskutočniť tiesňové volanie, stlačte Menu."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Telefón odomknete stlačením tlačidla Menu."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 02a748b3..5c554ab 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -652,7 +652,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Napačna koda PIN."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Če želite telefon odkleniti, pritisnite meni in nato 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Številka za klic v sili"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ni storitve."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ni signala"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Zaslon je zaklenjen."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Če želite odkleniti napravo ali opraviti klic v sili, pritisnite meni."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Če želite odkleniti, pritisnite meni."</string>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index 128dc23..45a4c68 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Kodi PIN është i pasaktë."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Për të shkyçur, shtyp \"Meny\" pastaj 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Numri i urgjencës"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nuk ka shërbim."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Nuk ka shërbim"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekrani është i kyçur."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Shtyp \"Meny\" për të shkyçur ose për të kryer telefonatë urgjence."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Shtyp \"Meny\" për të shkyçur."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index aec317e..71538de 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -651,7 +651,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN кôд је нетачан."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Да бисте откључали, притисните „Мени“, а затим 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Број за хитне случајеве"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Нема услуге."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Мобилна мрежа није доступна"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Екран је закључан."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Притисните „Мени“ да бисте откључали телефон или упутите хитан позив."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Притисните „Мени“ за откључавање."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 1032768..49a3862 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Fel PIN-kod."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Tryck på Menu och sedan på 0 om du vill låsa upp."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nödsamtalsnummer"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ingen tjänst."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ingen tjänst"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skärmen har låsts."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Tryck på Menu om du vill låsa upp eller ringa nödsamtal."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Tryck på Menu om du vill låsa upp."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 6cc7b13..1d54bcf 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -652,7 +652,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Msimbo wa PIN usio sahihi."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Ili kufungua, bofya Menyu kisha 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nambari ya dharura"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Hakuna huduma"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Hakuna huduma"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"skrini imefungwa."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Bonyeza Menyu ili kufungua au kupiga simu ya dharura."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Bonyeza Menyu ili kufungua."</string>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index 1cdd590..e520908 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"தவறான பின் குறியீடு."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"தடைநீக்க, மெனுவை அழுத்தி பின்பு 0 ஐ அழுத்தவும்."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"அவசர எண்"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"சேவை இல்லை."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"சேவை இல்லை"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"திரை பூட்டப்பட்டுள்ளது."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"தடைநீக்க மெனுவை அழுத்தவும் அல்லது அவசர அழைப்பை மேற்கொள்ளவும்."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"திறக்க, மெனுவை அழுத்தவும்."</string>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index 11c382e..fd3d5f2 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"చెల్లని పిన్‌ కోడ్."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"అన్‌లాక్ చేయడానికి, మెను ఆపై 0ని నొక్కండి."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"అత్యవసర నంబర్"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"సేవ లేదు."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"సేవ లేదు"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"స్క్రీన్ లాక్ చేయబడింది."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"అన్‌లాక్ చేయడానికి లేదా అత్యవసర కాల్ చేయడానికి మెను నొక్కండి."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"అన్‌లాక్ చేయడానికి మెను నొక్కండి."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index d061873..4b4af30 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"รหัส PIN ไม่ถูกต้อง"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"หากต้องการปลดล็อก กด เมนู ตามด้วย 0"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"หมายเลขฉุกเฉิน"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"ไม่มีบริการ"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"ไม่มีบริการ"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"หน้าจอถูกล็อก"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"กด เมนู เพื่อปลดล็อกหรือโทรฉุกเฉิน"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"กด เมนู เพื่อปลดล็อก"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index d87d7a9..afc71b9 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Maling PIN code."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Upang i-unlock, pindutin ang Menu pagkatapos ay 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Pang-emergency na numero"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Walang serbisyo."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Walang serbisyo"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Naka-lock ang screen."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Pindutin ang Menu upang i-unlock o magsagawa ng pang-emergency na tawag."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Pindutin ang Menu upang i-unlock."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index a5917e5..7cbf3d2 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Yanlış PIN kodu."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Kilidi açmak için önce Menü\'ye, sonra 0\'a basın."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Acil durum numarası"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Hizmet yok."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Hizmet yok"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekran kilitli."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Kilidi açmak veya acil çağrı yapmak için Menü\'ye basın."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Kilidi açmak için Menü\'ye basın."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 6f77952..9c4f867 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -652,7 +652,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Неправильний PIN-код."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Щоб розбл., натисн. меню та 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Аварійний номер"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Зв’язку немає."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Немає зв’язку"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Екран заблоков."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Натис. меню, щоб розбл. чи зробити авар. виклик."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Натисн. меню, щоб розбл."</string>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index 55e6efd..dd8aeca 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"‏غلط PIN کوڈ۔"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"غیر مقفل کرنے کیلئے، مینو پھر 0 دبائیں۔"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"ہنگامی نمبر"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"کوئی سروس نہیں ہے۔"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"کوئی سروس نہیں ہے"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"اسکرین مقفل ہے۔"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"غیر مقفل کرنے کیلئے مینو دبائیں یا ہنگامی کال کریں۔"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"غیر مقفل کرنے کیلئے مینو دبائیں۔"</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index c76df70..ee7da4e 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Noto‘g‘ri PIN-kod."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Qulfni ochish uchun avval \"Menu\"ni, so‘ngra 0 raqamini bosing."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Favqulodda raqam"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Aloqa yo‘q."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Xizmat mavjud emas"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekran qulflangan."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Qulfdan chiqarish yoki favqulodda qo‘ng‘iroqni amalga oshirish uchun \"Menyu\"ni bosing."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Qulfni ochish uchun \"Menyu\"ga bosing."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index a74e10e..5ae3e1e 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Mã PIN không chính xác."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Để mở khóa, hãy nhấn vào Menu sau đó nhấn 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Số khẩn cấp"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Không có dịch vụ nào."</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Không có dịch vụ nào"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Màn hình đã khóa."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Nhấn vào Menu để mở khóa hoặc thực hiện cuộc gọi khẩn cấp."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Nhấn vào Menu để mở khóa."</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 57c91a8..602f607 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -650,7 +650,8 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN码有误。"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"要解锁,请先按 MENU 再按 0。"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"急救或报警电话"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"无服务。"</string>
+    <!-- no translation found for lockscreen_carrier_default (6169005837238288522) -->
+    <skip />
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"屏幕已锁定。"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"按 Menu 解锁或进行紧急呼救。"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"按 MENU 解锁。"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 5b6b456..41952a7 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN 碼不正確。"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"如要解鎖,請按選單鍵,然後按 0。"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"緊急電話號碼"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"沒有服務。"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"沒有服務"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"螢幕已鎖定。"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"按選單鍵解鎖或撥打緊急電話。"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"按選單鍵解鎖。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 387f8bb..48ea636 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN 碼不正確。"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"如要解鎖,請按 Menu 鍵,然後按 0。"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"緊急電話號碼"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"沒有服務。"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"沒有服務"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"螢幕已鎖定。"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"按下 [Menu] 解鎖或撥打緊急電話。"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"按下 Menu 鍵解鎖。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index a9b6d14..f597e5d 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -650,7 +650,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Ikhodi ye-PIN engalungile!"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Ukuvula, chofoza Menyu bese 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Inombolo ephuthumayo"</string>
-    <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ayikho isevisi"</string>
+    <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ayikho isevisi"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Isikrini sivaliwe."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Chofoza Menyu ukuvula noma ukwenza ikholi ephuthumayo."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Chofoza Menyu ukuvula."</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 90306fd..8e36eb0 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1832,6 +1832,9 @@
         <enum name="KEYCODE_MEDIA_STEP_FORWARD" value="274" />
         <enum name="KEYCODE_MEDIA_STEP_BACKWARD" value="275" />
         <enum name="KEYCODE_SOFT_SLEEP" value="276" />
+        <enum name="KEYCODE_CUT" value="277" />
+        <enum name="KEYCODE_COPY" value="278" />
+        <enum name="KEYCODE_PASTE" value="279" />
     </attr>
 
     <!-- ***************************************************************** -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1c8062d..d20b09f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2359,4 +2359,8 @@
     <!-- The fraction of display size (lower of height and width) that will be used to determine
          the default minimal size for resizeable tasks. -->
     <fraction name="config_displayFractionForDefaultMinimalSizeOfResizeableTask">25%</fraction>
+
+    <!-- The BT name of the keyboard packaged with the device. If this is defined, SystemUI will
+         automatically try to pair with it when the device exits tablet mode. -->
+    <string translatable="false" name="config_packagedKeyboardName"></string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3ce8ec5..6f239e6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2333,4 +2333,6 @@
   <java-symbol type="string" name="config_iccHotswapPromptForRestartDialogComponent" />
 
   <java-symbol type="integer" name="config_windowResizingBackgroundColorARGB" />
+
+  <java-symbol type="string" name="config_packagedKeyboardName" />
 </resources>
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index f10ba96..2a10bdd 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -152,11 +152,11 @@
 # key 130 "KEY_PROPS"
 # key 131 "KEY_UNDO"
 # key 132 "KEY_FRONT"
-# key 133 "KEY_COPY"
+key 133   COPY
 # key 134 "KEY_OPEN"
-# key 135 "KEY_PASTE"
+key 135   PASTE
 # key 136 "KEY_FIND"
-# key 137 "KEY_CUT"
+key 137   CUT
 # key 138 "KEY_HELP"
 key 139   MENU
 key 140   CALCULATOR
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index e235a99..3ed6a78 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -379,6 +379,7 @@
                 r, theme, attrs, R.styleable.AnimatedStateListDrawable);
         super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedStateListDrawable_visible);
         updateStateFromTypedArray(a);
+        updateDensity(r);
         a.recycle();
 
         inflateChildElements(r, parser, attrs, theme);
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 521c74b..6bf3afd 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -291,6 +291,7 @@
         final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.AnimationDrawable);
         super.inflateWithAttributes(r, parser, a, R.styleable.AnimationDrawable_visible);
         updateStateFromTypedArray(a);
+        updateDensity(r);
         a.recycle();
 
         inflateChildElements(r, parser, attrs, theme);
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 1f7d996..ac72734 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -29,6 +29,7 @@
 import android.graphics.Rect;
 import android.graphics.PorterDuff.Mode;
 import android.os.SystemClock;
+import android.util.DisplayMetrics;
 import android.util.LayoutDirection;
 import android.util.SparseArray;
 import android.view.View;
@@ -581,6 +582,17 @@
         return mCurrDrawable;
     }
 
+    /**
+     * Updates the source density based on the resources used to inflate
+     * density-dependent values. Implementing classes should call this method
+     * during inflation.
+     *
+     * @param res the resources used to inflate density-dependent values
+     */
+    final void updateDensity(Resources res) {
+        mDrawableContainerState.updateDensity(res);
+    }
+
     @Override
     public void applyTheme(Theme theme) {
         mDrawableContainerState.applyTheme(theme);
@@ -638,22 +650,22 @@
      */
     public abstract static class DrawableContainerState extends ConstantState {
         final DrawableContainer mOwner;
-        final Resources mRes;
 
-        SparseArray<ConstantStateFuture> mDrawableFutures;
-
+        Resources mSourceRes;
+        int mDensity = DisplayMetrics.DENSITY_DEFAULT;
         int mChangingConfigurations;
         int mChildrenChangingConfigurations;
 
+        SparseArray<ConstantStateFuture> mDrawableFutures;
         Drawable[] mDrawables;
         int mNumChildren;
 
         boolean mVariablePadding = false;
-        boolean mPaddingChecked;
+        boolean mCheckedPadding;
         Rect mConstantPadding;
 
         boolean mConstantSize = false;
-        boolean mComputedConstantSize;
+        boolean mCheckedConstantSize;
         int mConstantWidth;
         int mConstantHeight;
         int mConstantMinimumWidth;
@@ -689,7 +701,13 @@
         DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
                 Resources res) {
             mOwner = owner;
-            mRes = res != null ? res : orig != null ? orig.mRes : null;
+
+            final Resources sourceRes = res != null ? res : (orig != null ? orig.mSourceRes : null);
+            mSourceRes = sourceRes;
+
+            final int densityDpi = sourceRes == null ? 0 : sourceRes.getDisplayMetrics().densityDpi;
+            final int sourceDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+            mDensity = sourceDensity;
 
             if (orig != null) {
                 mChangingConfigurations = orig.mChangingConfigurations;
@@ -713,21 +731,30 @@
                 mHasTintList = orig.mHasTintList;
                 mHasTintMode = orig.mHasTintMode;
 
-                // Cloning the following values may require creating futures.
-                mConstantPadding = orig.getConstantPadding();
-                mPaddingChecked = true;
+                if (orig.mDensity == sourceDensity) {
+                    if (orig.mCheckedPadding) {
+                        mConstantPadding = new Rect(orig.mConstantPadding);
+                        mCheckedPadding = true;
+                    }
 
-                mConstantWidth = orig.getConstantWidth();
-                mConstantHeight = orig.getConstantHeight();
-                mConstantMinimumWidth = orig.getConstantMinimumWidth();
-                mConstantMinimumHeight = orig.getConstantMinimumHeight();
-                mComputedConstantSize = true;
+                    if (orig.mCheckedConstantSize) {
+                        mConstantWidth = orig.mConstantWidth;
+                        mConstantHeight = orig.mConstantHeight;
+                        mConstantMinimumWidth = orig.mConstantMinimumWidth;
+                        mConstantMinimumHeight = orig.mConstantMinimumHeight;
+                        mCheckedConstantSize = true;
+                    }
+                }
 
-                mOpacity = orig.getOpacity();
-                mCheckedOpacity = true;
+                if (orig.mCheckedOpacity) {
+                    mOpacity = orig.mOpacity;
+                    mCheckedOpacity = true;
+                }
 
-                mStateful = orig.isStateful();
-                mCheckedStateful = true;
+                if (orig.mCheckedStateful) {
+                    mStateful = orig.mStateful;
+                    mCheckedStateful = true;
+                }
 
                 // Postpone cloning children and futures until we're absolutely
                 // sure that we're done computing values for the original state.
@@ -783,8 +810,8 @@
             mCheckedOpacity = false;
 
             mConstantPadding = null;
-            mPaddingChecked = false;
-            mComputedConstantSize = false;
+            mCheckedPadding = false;
+            mCheckedConstantSize = false;
 
             return pos;
         }
@@ -863,6 +890,31 @@
             return changed;
         }
 
+        /**
+         * Updates the source density based on the resources used to inflate
+         * density-dependent values.
+         *
+         * @param res the resources used to inflate density-dependent values
+         */
+        final void updateDensity(Resources res) {
+            if (mSourceRes != null) {
+                mSourceRes = res;
+            }
+
+            // The density may have changed since the last update (if any). Any
+            // dimension-type attributes will need their default values scaled.
+            final int densityDpi = res.getDisplayMetrics().densityDpi;
+            final int newSourceDensity = densityDpi == 0 ?
+                    DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+            final int oldSourceDensity = mDensity;
+            mDensity = newSourceDensity;
+
+            if (oldSourceDensity != newSourceDensity) {
+                mCheckedConstantSize = false;
+                mCheckedPadding = false;
+            }
+        }
+
         final void applyTheme(Theme theme) {
             if (theme != null) {
                 createAllFutures();
@@ -877,6 +929,8 @@
                         mChildrenChangingConfigurations |= drawables[i].getChangingConfigurations();
                     }
                 }
+
+                updateDensity(theme.getResources());
             }
         }
 
@@ -941,7 +995,7 @@
                 return null;
             }
 
-            if ((mConstantPadding != null) || mPaddingChecked) {
+            if ((mConstantPadding != null) || mCheckedPadding) {
                 return mConstantPadding;
             }
 
@@ -961,7 +1015,7 @@
                 }
             }
 
-            mPaddingChecked = true;
+            mCheckedPadding = true;
             return (mConstantPadding = r);
         }
 
@@ -974,7 +1028,7 @@
         }
 
         public final int getConstantWidth() {
-            if (!mComputedConstantSize) {
+            if (!mCheckedConstantSize) {
                 computeConstantSize();
             }
 
@@ -982,7 +1036,7 @@
         }
 
         public final int getConstantHeight() {
-            if (!mComputedConstantSize) {
+            if (!mCheckedConstantSize) {
                 computeConstantSize();
             }
 
@@ -990,7 +1044,7 @@
         }
 
         public final int getConstantMinimumWidth() {
-            if (!mComputedConstantSize) {
+            if (!mCheckedConstantSize) {
                 computeConstantSize();
             }
 
@@ -998,7 +1052,7 @@
         }
 
         public final int getConstantMinimumHeight() {
-            if (!mComputedConstantSize) {
+            if (!mCheckedConstantSize) {
                 computeConstantSize();
             }
 
@@ -1006,7 +1060,7 @@
         }
 
         protected void computeConstantSize() {
-            mComputedConstantSize = true;
+            mCheckedConstantSize = true;
 
             createAllFutures();
 
@@ -1146,10 +1200,10 @@
              */
             public Drawable get(DrawableContainerState state) {
                 final Drawable result;
-                if (state.mRes == null) {
+                if (state.mSourceRes == null) {
                     result = mConstantState.newDrawable();
                 } else {
-                    result = mConstantState.newDrawable(state.mRes);
+                    result = mConstantState.newDrawable(state.mSourceRes);
                 }
                 result.setLayoutDirection(state.mLayoutDirection);
                 result.setCallback(state.mOwner);
diff --git a/graphics/java/android/graphics/drawable/LevelListDrawable.java b/graphics/java/android/graphics/drawable/LevelListDrawable.java
index b01c643..4ce52d1 100644
--- a/graphics/java/android/graphics/drawable/LevelListDrawable.java
+++ b/graphics/java/android/graphics/drawable/LevelListDrawable.java
@@ -88,7 +88,13 @@
     public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
             throws XmlPullParserException, IOException {
         super.inflate(r, parser, attrs, theme);
+        updateDensity(r);
 
+        inflateChildElements(r, parser, attrs, theme);
+    }
+
+    private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
+            Theme theme) throws XmlPullParserException, IOException {
         int type;
 
         int low = 0;
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index 758410a..64a9eb5 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -110,6 +110,7 @@
         final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.StateListDrawable);
         super.inflateWithAttributes(r, parser, a, R.styleable.StateListDrawable_visible);
         updateStateFromTypedArray(a);
+        updateDensity(r);
         a.recycle();
 
         inflateChildElements(r, parser, attrs, theme);
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 5988781..e161f3d 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -222,11 +222,14 @@
     // caching the bitmap by default is allowed.
     private boolean mAllowCaching = true;
 
+    /** The density of the display on which this drawable will be rendered. */
+    private int mTargetDensity;
+
     // Given the virtual display setup, the dpi can be different than the inflation's dpi.
     // Therefore, we need to scale the values we got from the getDimension*().
     private int mDpiScaledWidth = 0;
     private int mDpiScaledHeight = 0;
-    private Insets mDpiScaleInsets = Insets.NONE;
+    private Insets mDpiScaledInsets = Insets.NONE;
 
     // Temp variable, only for saving "new" operation at the draw() time.
     private final float[] mTmpFloats = new float[9];
@@ -234,17 +237,37 @@
     private final Rect mTmpBounds = new Rect();
 
     public VectorDrawable() {
-        this(null, null);
+        this(new VectorDrawableState(), null);
     }
 
+    /**
+     * The one constructor to rule them all. This is called by all public
+     * constructors to set the state and initialize local properties.
+     */
     private VectorDrawable(@NonNull VectorDrawableState state, @Nullable Resources res) {
-        if (state == null) {
-            mVectorState = new VectorDrawableState();
+        mVectorState = state;
+
+        updateLocalState(res);
+    }
+
+    /**
+     * Initializes local dynamic properties from state. This should be called
+     * after significant state changes, e.g. from the One True Constructor and
+     * after inflating or applying a theme.
+     *
+     * @param res resources of the context in which the drawable will be
+     *            displayed, or {@code null} to use the constant state defaults
+     */
+    private void updateLocalState(Resources res) {
+        if (res != null) {
+            final int densityDpi = res.getDisplayMetrics().densityDpi;
+            mTargetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
         } else {
-            mVectorState = state;
-            mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+            mTargetDensity = mVectorState.mVPathRenderer.mSourceDensity;
         }
-        updateDimensionInfo(res, false);
+
+        mTintFilter = updateTintFilter(mTintFilter, mVectorState.mTint, mVectorState.mTintMode);
+        computeVectorSize();
     }
 
     @Override
@@ -416,55 +439,38 @@
     /** @hide */
     @Override
     public Insets getOpticalInsets() {
-        return mDpiScaleInsets;
+        return mDpiScaledInsets;
     }
 
     /*
-     * Update the VectorDrawable dimension since the res can be in different Dpi now.
-     * Basically, when a new instance is created or getDimension() is called, we should update
-     * the current VectorDrawable's dimension information.
-     * Only after updateStateFromTypedArray() is called, we should called this and update the
-     * constant state's dpi info, i.e. updateConstantStateDensity == true.
+     * Update local dimensions to adjust for a target density that may differ
+     * from the source density against which the constant state was loaded.
      */
-    void updateDimensionInfo(@Nullable Resources res, boolean updateConstantStateDensity) {
-        if (res != null) {
-            final int densityDpi = res.getDisplayMetrics().densityDpi;
-            final int targetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+    void computeVectorSize() {
+        final VPathRenderer pathRenderer = mVectorState.mVPathRenderer;
+        final Insets opticalInsets = pathRenderer.mOpticalInsets;
 
-            if (updateConstantStateDensity) {
-                mVectorState.mVPathRenderer.mTargetDensity = targetDensity;
-            } else {
-                final int constantStateDensity = mVectorState.mVPathRenderer.mTargetDensity;
-                if (targetDensity != constantStateDensity && constantStateDensity != 0) {
-                    mDpiScaledWidth = Bitmap.scaleFromDensity(
-                            (int) mVectorState.mVPathRenderer.mBaseWidth, constantStateDensity,
-                            targetDensity);
-                    mDpiScaledHeight = Bitmap.scaleFromDensity(
-                            (int) mVectorState.mVPathRenderer.mBaseHeight,constantStateDensity,
-                            targetDensity);
-                    final int left = Bitmap.scaleFromDensity(
-                            mVectorState.mVPathRenderer.mOpticalInsets.left, constantStateDensity,
-                            targetDensity);
-                    final int right = Bitmap.scaleFromDensity(
-                            mVectorState.mVPathRenderer.mOpticalInsets.right, constantStateDensity,
-                            targetDensity);
-                    final int top = Bitmap.scaleFromDensity(
-                            mVectorState.mVPathRenderer.mOpticalInsets.top, constantStateDensity,
-                            targetDensity);
-                    final int bottom = Bitmap.scaleFromDensity(
-                            mVectorState.mVPathRenderer.mOpticalInsets.bottom, constantStateDensity,
-                            targetDensity);
-                    mDpiScaleInsets = Insets.of(left, top, right, bottom);
-                    return;
-                }
-            }
+        final int sourceDensity = pathRenderer.mSourceDensity;
+        final int targetDensity = mTargetDensity;
+        if (targetDensity != sourceDensity) {
+            mDpiScaledWidth = Bitmap.scaleFromDensity(
+                    (int) pathRenderer.mBaseWidth, sourceDensity, targetDensity);
+            mDpiScaledHeight = Bitmap.scaleFromDensity(
+                    (int) pathRenderer.mBaseHeight,sourceDensity, targetDensity);
+            final int left = Bitmap.scaleFromDensity(
+                    opticalInsets.left, sourceDensity, targetDensity);
+            final int right = Bitmap.scaleFromDensity(
+                    opticalInsets.right, sourceDensity, targetDensity);
+            final int top = Bitmap.scaleFromDensity(
+                    opticalInsets.top, sourceDensity, targetDensity);
+            final int bottom = Bitmap.scaleFromDensity(
+                    opticalInsets.bottom, sourceDensity, targetDensity);
+            mDpiScaledInsets = Insets.of(left, top, right, bottom);
+        } else {
+            mDpiScaledWidth = (int) pathRenderer.mBaseWidth;
+            mDpiScaledHeight = (int) pathRenderer.mBaseHeight;
+            mDpiScaledInsets = opticalInsets;
         }
-        // For all the other cases, like either res is null, constant state is not initialized or
-        // target density is the same as the constant state, we will just use the constant state
-        // dimensions.
-        mDpiScaledWidth = (int) mVectorState.mVPathRenderer.mBaseWidth;
-        mDpiScaledHeight = (int) mVectorState.mVPathRenderer.mBaseHeight;
-        mDpiScaleInsets = mVectorState.mVPathRenderer.mOpticalInsets;
     }
 
     @Override
@@ -487,7 +493,6 @@
             try {
                 state.mCacheDirty = true;
                 updateStateFromTypedArray(a);
-                updateDimensionInfo(t.getResources(), true /* update constant state */);
             } catch (XmlPullParserException e) {
                 throw new RuntimeException(e);
             } finally {
@@ -505,8 +510,8 @@
             path.applyTheme(t);
         }
 
-        // Update local state.
-        mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+        // Update local properties.
+        updateLocalState(t.getResources());
     }
 
     /**
@@ -579,8 +584,8 @@
         state.mCacheDirty = true;
         inflateInternal(res, parser, attrs, theme);
 
-        mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
-        updateDimensionInfo(res, true /* update constant state */);
+        // Update local properties.
+        updateLocalState(res);
     }
 
     private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
@@ -593,6 +598,14 @@
         // Extract the theme attributes, if any.
         state.mThemeAttrs = a.extractThemeAttrs();
 
+        // The density may have changed since the last update (if any). Any
+        // dimension-type attributes will need their default values scaled.
+        final int densityDpi = a.getResources().getDisplayMetrics().densityDpi;
+        final int newSourceDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+        final int oldSourceDensity = pathRenderer.mSourceDensity;
+        final float densityScale = newSourceDensity / (float) oldSourceDensity;
+        pathRenderer.mSourceDensity = newSourceDensity;
+
         final int tintMode = a.getInt(R.styleable.VectorDrawable_tintMode, -1);
         if (tintMode != -1) {
             state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN);
@@ -620,9 +633,11 @@
         }
 
         pathRenderer.mBaseWidth = a.getDimension(
-                R.styleable.VectorDrawable_width, pathRenderer.mBaseWidth);
+                R.styleable.VectorDrawable_width,
+                pathRenderer.mBaseWidth * densityScale);
         pathRenderer.mBaseHeight = a.getDimension(
-                R.styleable.VectorDrawable_height, pathRenderer.mBaseHeight);
+                R.styleable.VectorDrawable_height,
+                pathRenderer.mBaseHeight * densityScale);
 
         if (pathRenderer.mBaseWidth <= 0) {
             throw new XmlPullParserException(a.getPositionDescription() +
@@ -633,13 +648,17 @@
         }
 
         final int insetLeft = a.getDimensionPixelSize(
-                R.styleable.VectorDrawable_opticalInsetLeft, pathRenderer.mOpticalInsets.left);
+                R.styleable.VectorDrawable_opticalInsetLeft,
+                (int) (pathRenderer.mOpticalInsets.left * densityScale));
         final int insetTop = a.getDimensionPixelSize(
-                R.styleable.VectorDrawable_opticalInsetTop, pathRenderer.mOpticalInsets.top);
+                R.styleable.VectorDrawable_opticalInsetTop,
+                (int) (pathRenderer.mOpticalInsets.top * densityScale));
         final int insetRight = a.getDimensionPixelSize(
-                R.styleable.VectorDrawable_opticalInsetRight, pathRenderer.mOpticalInsets.right);
+                R.styleable.VectorDrawable_opticalInsetRight,
+                (int) (pathRenderer.mOpticalInsets.right * densityScale));
         final int insetBottom = a.getDimensionPixelSize(
-                R.styleable.VectorDrawable_opticalInsetBottom, pathRenderer.mOpticalInsets.bottom);
+                R.styleable.VectorDrawable_opticalInsetBottom,
+                (int) (pathRenderer.mOpticalInsets.bottom * densityScale));
         pathRenderer.mOpticalInsets = Insets.of(insetLeft, insetTop, insetRight, insetBottom);
 
         final float alphaInFloat = a.getFloat(R.styleable.VectorDrawable_alpha,
@@ -917,7 +936,7 @@
         int mRootAlpha = 0xFF;
         String mRootName = null;
 
-        int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
+        int mSourceDensity = DisplayMetrics.DENSITY_DEFAULT;
 
         final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<>();
 
@@ -954,7 +973,7 @@
             mChangingConfigurations = copy.mChangingConfigurations;
             mRootAlpha = copy.mRootAlpha;
             mRootName = copy.mRootName;
-            mTargetDensity = copy.mTargetDensity;
+            mSourceDensity = copy.mSourceDensity;
             if (copy.mRootName != null) {
                 mVGTargetsMap.put(copy.mRootName, this);
             }
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 1a43658..842f575 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -263,4 +263,9 @@
     microbench/DisplayListCanvasBench.cpp \
     microbench/LinearAllocatorBench.cpp
 
+ifeq (true, $(HWUI_NEW_OPS))
+    LOCAL_SRC_FILES += \
+        microbench/OpReordererBench.cpp
+endif
+
 include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 4d9f9b4..94806ca 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -74,53 +74,64 @@
 #endif
 }
 
-void BakedOpRenderer::onRenderNodeOp(Info*, const RenderNodeOp&, const BakedOpState&) {
+void BakedOpRenderer::onRenderNodeOp(Info&, const RenderNodeOp&, const BakedOpState&) {
     LOG_ALWAYS_FATAL("unsupported operation");
 }
 
-void BakedOpRenderer::onBitmapOp(Info* info, const BitmapOp& op, const BakedOpState& state) {
-    info->caches.textureState().activateTexture(0); // TODO: should this be automatic, and/or elsewhere?
-    Texture* texture = info->getTexture(op.bitmap);
+void BakedOpRenderer::onBitmapOp(Info& info, const BitmapOp& op, const BakedOpState& state) {
+    info.caches.textureState().activateTexture(0); // TODO: should this be automatic, and/or elsewhere?
+    Texture* texture = info.getTexture(op.bitmap);
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
     const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
             ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
     Glop glop;
-    GlopBuilder(info->renderState, info->caches, &glop)
+    GlopBuilder(info.renderState, info.caches, &glop)
             .setRoundRectClipState(state.roundRectClipState)
             .setMeshTexturedUnitQuad(texture->uvMapper)
             .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
             .setTransform(state.computedState.transform, TransformFlags::None)
             .setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
             .build();
-    info->renderGlop(state, glop);
+    info.renderGlop(state, glop);
 }
 
-void BakedOpRenderer::onRectOp(Info* info, const RectOp& op, const BakedOpState& state) {
+void BakedOpRenderer::onRectOp(Info& info, const RectOp& op, const BakedOpState& state) {
     Glop glop;
-    GlopBuilder(info->renderState, info->caches, &glop)
+    GlopBuilder(info.renderState, info.caches, &glop)
             .setRoundRectClipState(state.roundRectClipState)
             .setMeshUnitQuad()
             .setFillPaint(*op.paint, state.alpha)
             .setTransform(state.computedState.transform, TransformFlags::None)
             .setModelViewMapUnitToRect(op.unmappedBounds)
             .build();
-    info->renderGlop(state, glop);
+    info.renderGlop(state, glop);
 }
 
-void BakedOpRenderer::onSimpleRectsOp(Info* info, const SimpleRectsOp& op, const BakedOpState& state) {
+void BakedOpRenderer::onSimpleRectsOp(Info& info, const SimpleRectsOp& op, const BakedOpState& state) {
     Glop glop;
-    GlopBuilder(info->renderState, info->caches, &glop)
+    GlopBuilder(info.renderState, info.caches, &glop)
             .setRoundRectClipState(state.roundRectClipState)
             .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
             .setFillPaint(*op.paint, state.alpha)
             .setTransform(state.computedState.transform, TransformFlags::None)
             .setModelViewOffsetRect(0, 0, op.unmappedBounds)
             .build();
-    info->renderGlop(state, glop);
+    info.renderGlop(state, glop);
 }
 
+void BakedOpRenderer::onBeginLayerOp(Info& info, const BeginLayerOp& op, const BakedOpState& state) {
+    LOG_ALWAYS_FATAL("unsupported operation");
+}
+
+void BakedOpRenderer::onEndLayerOp(Info& info, const EndLayerOp& op, const BakedOpState& state) {
+    LOG_ALWAYS_FATAL("unsupported operation");
+}
+
+void BakedOpRenderer::onLayerOp(Info& info, const LayerOp& op, const BakedOpState& state) {
+    LOG_ALWAYS_FATAL("unsupported operation");
+}
 
 } // namespace uirenderer
 } // namespace android
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index b8b4426..f45dbe4 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -65,7 +65,7 @@
      * These functions will perform the actual rendering of the individual operations in OpenGL,
      * given the transform/clip and other state built into the BakedOpState object passed in.
      */
-    #define BAKED_OP_RENDERER_METHOD(Type) static void on##Type(Info* info, const Type& op, const BakedOpState& state);
+    #define BAKED_OP_RENDERER_METHOD(Type) static void on##Type(Info& info, const Type& op, const BakedOpState& state);
     MAP_OPS(BAKED_OP_RENDERER_METHOD);
 };
 
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
index e2201ca..ddb8c84 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -68,7 +68,7 @@
         // resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
         clipRect = recordedOp.localClipRect;
         snapshot.transform->mapRect(clipRect);
-        clipRect.doIntersect(snapshot.getClipRect());
+        clipRect.doIntersect(snapshot.getRenderTargetClip());
         clipRect.snapToPixelBoundaries();
 
         // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index eca71c6..6a6cc42 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -259,7 +259,7 @@
     currentTransform()->mapRect(r);
     r.snapGeometryToPixelBoundaries(snapOut);
 
-    Rect clipRect(currentClipRect());
+    Rect clipRect(currentRenderTargetClip());
     clipRect.snapToPixelBoundaries();
 
     if (!clipRect.intersects(r)) return true;
@@ -287,7 +287,7 @@
     currentTransform()->mapRect(r);
     r.roundOut(); // rounded out to be conservative
 
-    Rect clipRect(currentClipRect());
+    Rect clipRect(currentRenderTargetClip());
     clipRect.snapToPixelBoundaries();
 
     if (!clipRect.intersects(r)) return true;
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
index be57f44..4709ef4 100644
--- a/libs/hwui/CanvasState.h
+++ b/libs/hwui/CanvasState.h
@@ -147,7 +147,7 @@
     void setInvisible(bool value) { mSnapshot->invisible = value; }
 
     inline const mat4* currentTransform() const { return currentSnapshot()->transform; }
-    inline const Rect& currentClipRect() const { return currentSnapshot()->getClipRect(); }
+    inline const Rect& currentRenderTargetClip() const { return currentSnapshot()->getRenderTargetClip(); }
     inline Region* currentRegion() const { return currentSnapshot()->region; }
     inline int currentFlags() const { return currentSnapshot()->flags; }
     const Vector3& currentLightCenter() const { return currentSnapshot()->getRelativeLightCenter(); }
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
index 7c0e257..c1417c4 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -52,7 +52,8 @@
     const std::vector<BakedOpState*>& getOps() const { return mOps; }
 
     void dump() const {
-        ALOGD("    Batch %p, merging %d, bounds " RECT_STRING, this, mMerging, RECT_ARGS(mBounds));
+        ALOGD("    Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING,
+                this, mBatchId, mMerging, mOps.size(), RECT_ARGS(mBounds));
     }
 protected:
     batchid_t mBatchId;
@@ -201,17 +202,106 @@
     Rect mClipRect;
 };
 
-class NullClient: public CanvasStateClient {
-    void onViewportInitialized() override {}
-    void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
-    GLuint getTargetFbo() const override { return 0; }
-};
-static NullClient sNullClient;
+// iterate back toward target to see if anything drawn since should overlap the new op
+// if no target, merging ops still interate to find similar batch to insert after
+void OpReorderer::LayerReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
+        BatchBase** targetBatch, size_t* insertBatchIndex) const {
+    for (int i = mBatches.size() - 1; i >= 0; i--) {
+        BatchBase* overBatch = mBatches[i];
+
+        if (overBatch == *targetBatch) break;
+
+        // TODO: also consider shader shared between batch types
+        if (batchId == overBatch->getBatchId()) {
+            *insertBatchIndex = i + 1;
+            if (!*targetBatch) break; // found insert position, quit
+        }
+
+        if (overBatch->intersects(clippedBounds)) {
+            // NOTE: it may be possible to optimize for special cases where two operations
+            // of the same batch/paint could swap order, such as with a non-mergeable
+            // (clipped) and a mergeable text operation
+            *targetBatch = nullptr;
+            break;
+        }
+    }
+}
+
+void OpReorderer::LayerReorderer::deferUnmergeableOp(LinearAllocator& allocator,
+        BakedOpState* op, batchid_t batchId) {
+    OpBatch* targetBatch = mBatchLookup[batchId];
+
+    size_t insertBatchIndex = mBatches.size();
+    if (targetBatch) {
+        locateInsertIndex(batchId, op->computedState.clippedBounds,
+                (BatchBase**)(&targetBatch), &insertBatchIndex);
+    }
+
+    if (targetBatch) {
+        targetBatch->batchOp(op);
+    } else  {
+        // new non-merging batch
+        targetBatch = new (allocator) OpBatch(batchId, op);
+        mBatchLookup[batchId] = targetBatch;
+        mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
+    }
+}
+
+// insertion point of a new batch, will hopefully be immediately after similar batch
+// (generally, should be similar shader)
+void OpReorderer::LayerReorderer::deferMergeableOp(LinearAllocator& allocator,
+        BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
+    MergingOpBatch* targetBatch = nullptr;
+
+    // Try to merge with any existing batch with same mergeId
+    auto getResult = mMergingBatchLookup[batchId].find(mergeId);
+    if (getResult != mMergingBatchLookup[batchId].end()) {
+        targetBatch = getResult->second;
+        if (!targetBatch->canMergeWith(op)) {
+            targetBatch = nullptr;
+        }
+    }
+
+    size_t insertBatchIndex = mBatches.size();
+    locateInsertIndex(batchId, op->computedState.clippedBounds,
+            (BatchBase**)(&targetBatch), &insertBatchIndex);
+
+    if (targetBatch) {
+        targetBatch->mergeOp(op);
+    } else  {
+        // new merging batch
+        targetBatch = new (allocator) MergingOpBatch(batchId, op);
+        mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch));
+
+        mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
+    }
+}
+
+void OpReorderer::LayerReorderer::replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers) const {
+    for (const BatchBase* batch : mBatches) {
+        // TODO: different behavior based on batch->isMerging()
+        for (const BakedOpState* op : batch->getOps()) {
+            receivers[op->op->opId](arg, *op->op, *op);
+        }
+    }
+}
+
+void OpReorderer::LayerReorderer::dump() const {
+    for (const BatchBase* batch : mBatches) {
+        batch->dump();
+    }
+}
 
 OpReorderer::OpReorderer()
-        : mCanvasState(sNullClient) {
+        : mCanvasState(*this) {
+    mLayerReorderers.emplace_back();
+    mLayerStack.push_back(0);
 }
 
+void OpReorderer::onViewportInitialized() {}
+
+void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
+
 void OpReorderer::defer(const SkRect& clip, int viewportWidth, int viewportHeight,
         const std::vector< sp<RenderNode> >& nodes) {
     mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
@@ -244,11 +334,11 @@
  * This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas. E.g. a
  * BitmapOp op then would be dispatched to OpReorderer::onBitmapOp(const BitmapOp&)
  */
-#define OP_RECIEVER(Type) \
+#define OP_RECEIVER(Type) \
         [](OpReorderer& reorderer, const RecordedOp& op) { reorderer.on##Type(static_cast<const Type&>(op)); },
 void OpReorderer::deferImpl(const DisplayList& displayList) {
     static std::function<void(OpReorderer& reorderer, const RecordedOp&)> receivers[] = {
-        MAP_OPS(OP_RECIEVER)
+        MAP_OPS(OP_RECEIVER)
     };
     for (const DisplayList::Chunk& chunk : displayList.getChunks()) {
         for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
@@ -260,23 +350,18 @@
 
 void OpReorderer::replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers) {
     ATRACE_NAME("flush drawing commands");
-    for (const BatchBase* batch : mBatches) {
-        // TODO: different behavior based on batch->isMerging()
-        for (const BakedOpState* op : batch->getOps()) {
-            receivers[op->op->opId](arg, *op->op, *op);
-        }
+    // Relay through layers in reverse order, since layers
+    // later in the list will be drawn by earlier ones
+    for (int i = mLayerReorderers.size() - 1; i >= 0; i--) {
+        mLayerReorderers[i].replayBakedOpsImpl(arg, receivers);
     }
 }
 
-BakedOpState* OpReorderer::bakeOpState(const RecordedOp& recordedOp) {
-    return BakedOpState::tryConstruct(mAllocator, *mCanvasState.currentSnapshot(), recordedOp);
-}
-
 void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
     if (op.renderNode->nothingToDraw()) {
         return;
     }
-    mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+    int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
 
     // apply state from RecordedOp
     mCanvasState.concatMatrix(op.localMatrix);
@@ -285,10 +370,10 @@
 
     // apply RenderProperties state
     if (op.renderNode->applyViewProperties(mCanvasState)) {
-        // not rejected do ops...
+        // if node not rejected based on properties, do ops...
         deferImpl(op.renderNode->getDisplayList());
     }
-    mCanvasState.restore();
+    mCanvasState.restoreToCount(count);
 }
 
 static batchid_t tessellatedBatchId(const SkPaint& paint) {
@@ -298,104 +383,70 @@
 }
 
 void OpReorderer::onBitmapOp(const BitmapOp& op) {
-    BakedOpState* bakedStateOp = bakeOpState(op);
+    BakedOpState* bakedStateOp = tryBakeOpState(op);
     if (!bakedStateOp) return; // quick rejected
 
     mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
     // TODO: AssetAtlas
-
-    deferMergeableOp(bakedStateOp, OpBatchType::Bitmap, mergeId);
+    currentLayer().deferMergeableOp(mAllocator, bakedStateOp, OpBatchType::Bitmap, mergeId);
 }
 
 void OpReorderer::onRectOp(const RectOp& op) {
-    BakedOpState* bakedStateOp = bakeOpState(op);
+    BakedOpState* bakedStateOp = tryBakeOpState(op);
     if (!bakedStateOp) return; // quick rejected
-    deferUnmergeableOp(bakedStateOp, tessellatedBatchId(*op.paint));
+    currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, tessellatedBatchId(*op.paint));
 }
 
 void OpReorderer::onSimpleRectsOp(const SimpleRectsOp& op) {
-    BakedOpState* bakedStateOp = bakeOpState(op);
+    BakedOpState* bakedStateOp = tryBakeOpState(op);
     if (!bakedStateOp) return; // quick rejected
-    deferUnmergeableOp(bakedStateOp, OpBatchType::Vertices);
+    currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, OpBatchType::Vertices);
 }
 
-// iterate back toward target to see if anything drawn since should overlap the new op
-// if no target, merging ops still interate to find similar batch to insert after
-void OpReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
-        BatchBase** targetBatch, size_t* insertBatchIndex) const {
-    for (int i = mBatches.size() - 1; i >= mEarliestBatchIndex; i--) {
-        BatchBase* overBatch = mBatches[i];
+// TODO: test rejection at defer time, where the bounds become empty
+void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
+    mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+    mCanvasState.writableSnapshot()->transform->loadIdentity();
+    mCanvasState.writableSnapshot()->initializeViewport(
+            (int) op.unmappedBounds.getWidth(), (int) op.unmappedBounds.getHeight());
+    mCanvasState.writableSnapshot()->roundRectClipState = nullptr;
 
-        if (overBatch == *targetBatch) break;
+    // create a new layer, and push its index on the stack
+    mLayerStack.push_back(mLayerReorderers.size());
+    mLayerReorderers.emplace_back();
+    mLayerReorderers.back().beginLayerOp = &op;
+}
 
-        // TODO: also consider shader shared between batch types
-        if (batchId == overBatch->getBatchId()) {
-            *insertBatchIndex = i + 1;
-            if (!*targetBatch) break; // found insert position, quit
-        }
+void OpReorderer::onEndLayerOp(const EndLayerOp& /* ignored */) {
+    mCanvasState.restore();
 
-        if (overBatch->intersects(clippedBounds)) {
-            // NOTE: it may be possible to optimize for special cases where two operations
-            // of the same batch/paint could swap order, such as with a non-mergeable
-            // (clipped) and a mergeable text operation
-            *targetBatch = nullptr;
-            break;
-        }
+    const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
+
+    // pop finished layer off of the stack
+    int finishedLayerIndex = mLayerStack.back();
+    mLayerStack.pop_back();
+
+    // record the draw operation into the previous layer's list of draw commands
+    // uses state from the associated beginLayerOp, since it has all the state needed for drawing
+    LayerOp* drawLayerOp = new (mAllocator) LayerOp(
+            beginLayerOp.unmappedBounds,
+            beginLayerOp.localMatrix,
+            beginLayerOp.localClipRect,
+            beginLayerOp.paint);
+    BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
+
+    if (bakedOpState) {
+        // Layer will be drawn into parent layer (which is now current, since we popped mLayerStack)
+        currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap);
+    } else {
+        // Layer won't be drawn - delete its drawing batches to prevent it from doing any work
+        mLayerReorderers[finishedLayerIndex].clear();
+        return;
     }
 }
 
-void OpReorderer::deferUnmergeableOp(BakedOpState* op, batchid_t batchId) {
-    OpBatch* targetBatch = mBatchLookup[batchId];
-
-    size_t insertBatchIndex = mBatches.size();
-    if (targetBatch) {
-        locateInsertIndex(batchId, op->computedState.clippedBounds,
-                (BatchBase**)(&targetBatch), &insertBatchIndex);
-    }
-
-    if (targetBatch) {
-        targetBatch->batchOp(op);
-    } else  {
-        // new non-merging batch
-        targetBatch = new (mAllocator) OpBatch(batchId, op);
-        mBatchLookup[batchId] = targetBatch;
-        mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
-    }
-}
-
-// insertion point of a new batch, will hopefully be immediately after similar batch
-// (generally, should be similar shader)
-void OpReorderer::deferMergeableOp(BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
-    MergingOpBatch* targetBatch = nullptr;
-
-    // Try to merge with any existing batch with same mergeId
-    auto getResult = mMergingBatches[batchId].find(mergeId);
-    if (getResult != mMergingBatches[batchId].end()) {
-        targetBatch = getResult->second;
-        if (!targetBatch->canMergeWith(op)) {
-            targetBatch = nullptr;
-        }
-    }
-
-    size_t insertBatchIndex = mBatches.size();
-    locateInsertIndex(batchId, op->computedState.clippedBounds,
-            (BatchBase**)(&targetBatch), &insertBatchIndex);
-
-    if (targetBatch) {
-        targetBatch->mergeOp(op);
-    } else  {
-        // new merging batch
-        targetBatch = new (mAllocator) MergingOpBatch(batchId, op);
-        mMergingBatches[batchId].insert(std::make_pair(mergeId, targetBatch));
-
-        mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
-    }
-}
-
-void OpReorderer::dump() {
-    for (const BatchBase* batch : mBatches) {
-        batch->dump();
-    }
+void OpReorderer::onLayerOp(const LayerOp& op) {
+    LOG_ALWAYS_FATAL("unsupported");
 }
 
 } // namespace uirenderer
diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h
index 6776a3c..73dc9af 100644
--- a/libs/hwui/OpReorderer.h
+++ b/libs/hwui/OpReorderer.h
@@ -54,19 +54,63 @@
     };
 }
 
-class OpReorderer {
+class OpReorderer : public CanvasStateClient {
+    typedef std::function<void(void*, const RecordedOp&, const BakedOpState&)> BakedOpReceiver;
+
+    /**
+     * Stores the deferred render operations and state used to compute ordering
+     * for a single FBO/layer.
+     */
+    class LayerReorderer {
+    public:
+        // iterate back toward target to see if anything drawn since should overlap the new op
+        // if no target, merging ops still iterate to find similar batch to insert after
+        void locateInsertIndex(int batchId, const Rect& clippedBounds,
+                BatchBase** targetBatch, size_t* insertBatchIndex) const;
+
+        void deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId);
+
+        // insertion point of a new batch, will hopefully be immediately after similar batch
+        // (generally, should be similar shader)
+        void deferMergeableOp(LinearAllocator& allocator,
+                BakedOpState* op, batchid_t batchId, mergeid_t mergeId);
+
+        void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers) const;
+
+        void clear() {
+            mBatches.clear();
+        }
+
+        void dump() const;
+
+        const BeginLayerOp* beginLayerOp = nullptr;
+
+    private:
+        std::vector<BatchBase*> mBatches;
+
+        /**
+         * Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
+         * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
+         * collide, which avoids the need to resolve mergeid collisions.
+         */
+        std::unordered_map<mergeid_t, MergingOpBatch*> mMergingBatchLookup[OpBatchType::Count];
+
+        // Maps batch ids to the most recent *non-merging* batch of that id
+        OpBatch* mBatchLookup[OpBatchType::Count] = { nullptr };
+
+    };
 public:
     OpReorderer();
+    virtual ~OpReorderer() {}
 
     // TODO: not final, just presented this way for simplicity. Layers too?
     void defer(const SkRect& clip, int viewportWidth, int viewportHeight,
             const std::vector< sp<RenderNode> >& nodes);
 
     void defer(int viewportWidth, int viewportHeight, const DisplayList& displayList);
-    typedef std::function<void(void*, const RecordedOp&, const BakedOpState&)> BakedOpReceiver;
 
     /**
-     * replayBakedOps() is templated based on what class will recieve ops being replayed.
+     * replayBakedOps() is templated based on what class will receive ops being replayed.
      *
      * It constructs a lookup array of lambdas, which allows a recorded BakeOpState to use
      * state->op->opId to lookup a receiver that will be called when the op is replayed.
@@ -77,19 +121,37 @@
      */
 #define BAKED_OP_RECEIVER(Type) \
     [](void* internalArg, const RecordedOp& op, const BakedOpState& state) { \
-        StaticReceiver::on##Type(static_cast<Arg*>(internalArg), static_cast<const Type&>(op), state); \
+        StaticReceiver::on##Type(*(static_cast<Arg*>(internalArg)), static_cast<const Type&>(op), state); \
     },
     template <typename StaticReceiver, typename Arg>
-    void replayBakedOps(Arg* arg) {
+    void replayBakedOps(Arg& arg) {
         static BakedOpReceiver receivers[] = {
             MAP_OPS(BAKED_OP_RECEIVER)
         };
-        StaticReceiver::startFrame(*arg);
-        replayBakedOpsImpl((void*)arg, receivers);
-        StaticReceiver::endFrame(*arg);
+        StaticReceiver::startFrame(arg);
+        replayBakedOpsImpl((void*)&arg, receivers);
+        StaticReceiver::endFrame(arg);
     }
+
+    void dump() const {
+        for (auto&& layer : mLayerReorderers) {
+            layer.dump();
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    /// CanvasStateClient interface
+    ///////////////////////////////////////////////////////////////////
+    virtual void onViewportInitialized() override;
+    virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override;
+    virtual GLuint getTargetFbo() const override { return 0; }
+
 private:
-    BakedOpState* bakeOpState(const RecordedOp& recordedOp);
+    LayerReorderer& currentLayer() { return mLayerReorderers[mLayerStack.back()]; }
+
+    BakedOpState* tryBakeOpState(const RecordedOp& recordedOp) {
+        return BakedOpState::tryConstruct(mAllocator, *mCanvasState.currentSnapshot(), recordedOp);
+    }
 
     void deferImpl(const DisplayList& displayList);
 
@@ -105,36 +167,27 @@
     void on##Type(const Type& op);
     MAP_OPS(INTERNAL_OP_HANDLER)
 
-    // iterate back toward target to see if anything drawn since should overlap the new op
-    // if no target, merging ops still iterate to find similar batch to insert after
-    void locateInsertIndex(int batchId, const Rect& clippedBounds,
-            BatchBase** targetBatch, size_t* insertBatchIndex) const;
+    // List of every deferred layer's render state. Replayed in reverse order to render a frame.
+    std::vector<LayerReorderer> mLayerReorderers;
 
-    void deferUnmergeableOp(BakedOpState* op, batchid_t batchId);
+    /*
+     * Stack of indices within mLayerReorderers representing currently active layers. If drawing
+     * layerA within a layerB, will contain, in order:
+     *  - 0 (representing FBO 0, always present)
+     *  - layerB's index
+     *  - layerA's index
+     *
+     * Note that this doesn't vector doesn't always map onto all values of mLayerReorderers. When a
+     * layer is finished deferring, it will still be represented in mLayerReorderers, but it's index
+     * won't be in mLayerStack. This is because it can be replayed, but can't have any more drawing
+     * ops added to it.
+    */
+    std::vector<size_t> mLayerStack;
 
-    // insertion point of a new batch, will hopefully be immediately after similar batch
-    // (generally, should be similar shader)
-    void deferMergeableOp(BakedOpState* op, batchid_t batchId, mergeid_t mergeId);
-
-    void dump();
-
-    std::vector<BatchBase*> mBatches;
-
-    /**
-     * Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
-     * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
-     * collide, which avoids the need to resolve mergeid collisions.
-     */
-    std::unordered_map<mergeid_t, MergingOpBatch*> mMergingBatches[OpBatchType::Count];
-
-    // Maps batch ids to the most recent *non-merging* batch of that id
-    OpBatch* mBatchLookup[OpBatchType::Count] = { nullptr };
     CanvasState mCanvasState;
 
     // contains ResolvedOps and Batches
     LinearAllocator mAllocator;
-
-    int mEarliestBatchIndex = 0;
 };
 
 }; // namespace uirenderer
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index cd03ac4..d4f65b6 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -223,7 +223,7 @@
 void OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
     if (mState.currentlyIgnored()) return;
 
-    Rect clip(mState.currentClipRect());
+    Rect clip(mState.currentRenderTargetClip());
     clip.snapToPixelBoundaries();
 
     // Since we don't know what the functor will draw, let's dirty
@@ -488,7 +488,7 @@
     currentTransform()->mapRect(bounds);
 
     // Layers only make sense if they are in the framebuffer's bounds
-    bounds.doIntersect(mState.currentClipRect());
+    bounds.doIntersect(mState.currentRenderTargetClip());
     if (!bounds.isEmpty()) {
         // We cannot work with sub-pixels in this case
         bounds.snapToPixelBoundaries();
@@ -1036,7 +1036,7 @@
 }
 
 void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
-    bounds.doIntersect(mState.currentClipRect());
+    bounds.doIntersect(mState.currentRenderTargetClip());
     if (!bounds.isEmpty()) {
         bounds.snapToPixelBoundaries();
         android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
@@ -1084,7 +1084,7 @@
                 .setMeshIndexedQuads(&mesh[0], quadCount)
                 .setFillClear()
                 .setTransform(*currentSnapshot(), transformFlags)
-                .setModelViewOffsetRect(0, 0, Rect(currentSnapshot()->getClipRect()))
+                .setModelViewOffsetRect(0, 0, Rect(currentSnapshot()->getRenderTargetClip()))
                 .build();
         renderGlop(glop, GlopRenderType::LayerClear);
 
@@ -1099,7 +1099,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDeferFlags) {
-    const Rect& currentClip = mState.currentClipRect();
+    const Rect& currentClip = mState.currentRenderTargetClip();
     const mat4* currentMatrix = currentTransform();
 
     if (stateDeferFlags & kStateDeferFlag_Draw) {
@@ -1187,7 +1187,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void OpenGLRenderer::setScissorFromClip() {
-    Rect clip(mState.currentClipRect());
+    Rect clip(mState.currentRenderTargetClip());
     clip.snapToPixelBoundaries();
 
     if (mRenderState.scissor().set(clip.left, getViewportHeight() - clip.bottom,
@@ -1430,7 +1430,7 @@
             return;
         }
 
-        DeferredDisplayList deferredList(mState.currentClipRect());
+        DeferredDisplayList deferredList(mState.currentRenderTargetClip());
         DeferStateStruct deferStruct(deferredList, *this, replayFlags);
         renderNode->defer(deferStruct, 0);
 
@@ -1765,7 +1765,7 @@
     // No need to check against the clip, we fill the clip region
     if (mState.currentlyIgnored()) return;
 
-    Rect clip(mState.currentClipRect());
+    Rect clip(mState.currentRenderTargetClip());
     clip.snapToPixelBoundaries();
 
     SkPaint paint;
@@ -2030,7 +2030,7 @@
     }
     fontRenderer.setTextureFiltering(linearFilter);
 
-    const Rect& clip(pureTranslate ? writableSnapshot()->getClipRect() : writableSnapshot()->getLocalClip());
+    const Rect& clip(pureTranslate ? writableSnapshot()->getRenderTargetClip() : writableSnapshot()->getLocalClip());
     Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
 
     TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
@@ -2191,7 +2191,7 @@
     fontRenderer.setTextureFiltering(linearFilter);
 
     // TODO: Implement better clipping for scaled/rotated text
-    const Rect* clip = !pureTranslate ? nullptr : &mState.currentClipRect();
+    const Rect* clip = !pureTranslate ? nullptr : &mState.currentRenderTargetClip();
     Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
 
     bool status;
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 36a8dac..c0c61db 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -29,7 +29,6 @@
 bool Properties::debugOverdraw = false;
 bool Properties::showDirtyRegions = false;
 bool Properties::skipEmptyFrames = true;
-bool Properties::swapBuffersWithDamage = true;
 bool Properties::useBufferAge = true;
 bool Properties::enablePartialUpdates = true;
 
@@ -117,7 +116,6 @@
     }
 
     skipEmptyFrames = property_get_bool(PROPERTY_SKIP_EMPTY_DAMAGE, true);
-    swapBuffersWithDamage = property_get_bool(PROPERTY_SWAP_WITH_DAMAGE, true);
     useBufferAge = property_get_bool(PROPERTY_USE_BUFFER_AGE, true);
     enablePartialUpdates = property_get_bool(PROPERTY_ENABLE_PARTIAL_UPDATES, true);
 
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 3512c36..74cd74b 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -142,13 +142,6 @@
 #define PROPERTY_SKIP_EMPTY_DAMAGE "debug.hwui.skip_empty_damage"
 
 /**
- * Setting this property will enable or disable usage of EGL_KHR_swap_buffers_with_damage
- * See: https://www.khronos.org/registry/egl/extensions/KHR/EGL_KHR_swap_buffers_with_damage.txt
- * Default is "true"
- */
-#define PROPERTY_SWAP_WITH_DAMAGE "debug.hwui.swap_with_damage"
-
-/**
  * Controls whether or not HWUI will use the EGL_EXT_buffer_age extension
  * to do partial invalidates. Setting this to "false" will fall back to
  * using BUFFER_PRESERVED instead
@@ -271,8 +264,6 @@
     static bool showDirtyRegions;
     // TODO: Remove after stabilization period
     static bool skipEmptyFrames;
-    // TODO: Remove after stabilization period
-    static bool swapBuffersWithDamage;
     static bool useBufferAge;
     static bool enablePartialUpdates;
 
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index a69f030..dd01637 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -41,7 +41,10 @@
         OP_FN(BitmapOp) \
         OP_FN(RectOp) \
         OP_FN(RenderNodeOp) \
-        OP_FN(SimpleRectsOp)
+        OP_FN(SimpleRectsOp) \
+        OP_FN(BeginLayerOp) \
+        OP_FN(EndLayerOp) \
+        OP_FN(LayerOp)
 
 // Generate OpId enum
 #define IDENTITY_FN(Type) Type,
@@ -112,6 +115,31 @@
     const size_t vertexCount;
 };
 
+/**
+ * Stateful operation! denotes the creation of an off-screen layer,
+ * and that commands following will render into it.
+ */
+struct BeginLayerOp : RecordedOp {
+    BeginLayerOp(BASE_PARAMS)
+            : SUPER(BeginLayerOp) {}
+};
+
+/**
+ * Stateful operation! Denotes end of off-screen layer, and that
+ * commands since last BeginLayerOp should be drawn into parent FBO.
+ *
+ * State in this op is empty, it just serves to signal that a layer has been finished.
+ */
+struct EndLayerOp : RecordedOp {
+    EndLayerOp()
+            : RecordedOp(RecordedOpId::EndLayerOp, Rect(0, 0), Matrix4::identity(), Rect(0, 0), nullptr) {}
+};
+
+struct LayerOp : RecordedOp {
+    LayerOp(BASE_PARAMS)
+            : SUPER(LayerOp) {}
+};
+
 }; // namespace uirenderer
 }; // namespace android
 
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 3b413aa..1f113bc 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -73,6 +73,20 @@
 }
 
 // ----------------------------------------------------------------------------
+// CanvasStateClient implementation
+// ----------------------------------------------------------------------------
+
+void RecordingCanvas::onViewportInitialized() {
+
+}
+
+void RecordingCanvas::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {
+    if (removed.flags & Snapshot::kFlagIsFboLayer) {
+        addOp(new (alloc()) EndLayerOp());
+    }
+}
+
+// ----------------------------------------------------------------------------
 // android/graphics/Canvas state operations
 // ----------------------------------------------------------------------------
 // Save (layer)
@@ -97,8 +111,66 @@
 
 int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, const SkPaint* paint,
         SkCanvas::SaveFlags flags) {
-    LOG_ALWAYS_FATAL("TODO");
-    return 0;
+    if (!(flags & SkCanvas::kClipToLayer_SaveFlag)) {
+        LOG_ALWAYS_FATAL("unclipped layers not supported");
+    }
+    // force matrix/clip isolation for layer
+    flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag;
+
+
+    const Snapshot& previous = *mState.currentSnapshot();
+
+    // initialize the snapshot as though it almost represents an FBO layer so deferred draw
+    // operations will be able to store and restore the current clip and transform info, and
+    // quick rejection will be correct (for display lists)
+
+    const Rect untransformedBounds(left, top, right, bottom);
+
+    // determine clipped bounds relative to previous viewport.
+    Rect visibleBounds = untransformedBounds;
+    previous.transform->mapRect(visibleBounds);
+
+
+    visibleBounds.doIntersect(previous.getRenderTargetClip());
+    visibleBounds.snapToPixelBoundaries();
+
+    Rect previousViewport(0, 0, previous.getViewportWidth(), previous.getViewportHeight());
+    visibleBounds.doIntersect(previousViewport);
+
+    // Map visible bounds back to layer space, and intersect with parameter bounds
+    Rect layerBounds = visibleBounds;
+    Matrix4 inverse;
+    inverse.loadInverse(*previous.transform);
+    inverse.mapRect(layerBounds);
+    layerBounds.doIntersect(untransformedBounds);
+
+    int saveValue = mState.save((int) flags);
+    Snapshot& snapshot = *mState.writableSnapshot();
+
+    // layerBounds is now original bounds, but with clipped to clip
+    // and viewport to ensure it's minimal size.
+    if (layerBounds.isEmpty() || untransformedBounds.isEmpty()) {
+        // Don't bother recording layer, since it's been rejected
+        snapshot.resetClip(0, 0, 0, 0);
+        return saveValue;
+    }
+
+    snapshot.flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer;
+    snapshot.initializeViewport(untransformedBounds.getWidth(), untransformedBounds.getHeight());
+    snapshot.resetTransform(-untransformedBounds.left, -untransformedBounds.top, 0.0f);
+
+    Rect clip = layerBounds;
+    clip.translate(-untransformedBounds.left, -untransformedBounds.top);
+    snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom);
+    snapshot.roundRectClipState = nullptr;
+
+    addOp(new (alloc()) BeginLayerOp(
+            Rect(left, top, right, bottom),
+            *previous.transform, // transform to *draw* with
+            previous.getRenderTargetClip(), // clip to *draw* with
+            refPaint(paint)));
+
+    return saveValue;
 }
 
 // Matrix
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 2179e4c..9c32b1a 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -52,8 +52,8 @@
 // ----------------------------------------------------------------------------
 // CanvasStateClient interface
 // ----------------------------------------------------------------------------
-    virtual void onViewportInitialized() override {}
-    virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override {}
+    virtual void onViewportInitialized() override;
+    virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override;
     virtual GLuint getTargetFbo() const override { return -1; }
 
 // ----------------------------------------------------------------------------
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 894a2bd..351fbaa 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -929,7 +929,7 @@
     const RenderProperties& backgroundProps = backgroundOp->renderNode->properties();
     renderer.translate(backgroundProps.getTranslationX(), backgroundProps.getTranslationY());
 
-    // If the projection reciever has an outline, we mask projected content to it
+    // If the projection receiver has an outline, we mask projected content to it
     // (which we know, apriori, are all tessellated paths)
     renderer.setProjectionPathMask(alloc, projectionReceiverOutline);
 
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index f824cc0..abef806 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -203,8 +203,8 @@
         return RP_SET(mPrimitiveFields.mProjectBackwards, shouldProject);
     }
 
-    bool setProjectionReceiver(bool shouldRecieve) {
-        return RP_SET(mPrimitiveFields.mProjectionReceiver, shouldRecieve);
+    bool setProjectionReceiver(bool shouldReceive) {
+        return RP_SET(mPrimitiveFields.mProjectionReceiver, shouldReceive);
     }
 
     bool isProjectionReceiver() const {
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index aeeda96..4789b33 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -158,13 +158,12 @@
     /**
      * Returns the current clip in render target coordinates.
      */
-    const Rect& getRenderTargetClip() { return mClipArea->getClipRect(); }
+    const Rect& getRenderTargetClip() const { return mClipArea->getClipRect(); }
 
     /*
      * Accessor functions so that the clip area can stay private
      */
     bool clipIsEmpty() const { return mClipArea->isEmpty(); }
-    const Rect& getClipRect() const { return mClipArea->getClipRect(); }
     const SkRegion& getClipRegion() const { return mClipArea->getClipRegion(); }
     bool clipIsSimple() const { return mClipArea->isSimple(); }
     const ClipArea& getClipArea() const { return *mClipArea; }
diff --git a/libs/hwui/microbench/OpReordererBench.cpp b/libs/hwui/microbench/OpReordererBench.cpp
new file mode 100644
index 0000000..cf96d44
--- /dev/null
+++ b/libs/hwui/microbench/OpReordererBench.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <benchmark/Benchmark.h>
+
+#include "BakedOpState.h"
+#include "BakedOpRenderer.h"
+#include "OpReorderer.h"
+#include "RecordedOp.h"
+#include "RecordingCanvas.h"
+#include "unit_tests/TestUtils.h"
+#include "microbench/MicroBench.h"
+
+#include <vector>
+
+using namespace android;
+using namespace android::uirenderer;
+
+auto sReorderingDisplayList = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+    SkBitmap bitmap = TestUtils::createSkBitmap(10, 10);
+    SkPaint paint;
+
+    // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
+    // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
+    canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    for (int i = 0; i < 30; i++) {
+        canvas.translate(0, 10);
+        canvas.drawRect(0, 0, 10, 10, paint);
+        canvas.drawBitmap(bitmap, 5, 0, nullptr);
+    }
+    canvas.restore();
+});
+
+BENCHMARK_NO_ARG(BM_OpReorderer_defer);
+void BM_OpReorderer_defer::Run(int iters) {
+    StartBenchmarkTiming();
+    for (int i = 0; i < iters; i++) {
+        OpReorderer reorderer;
+        reorderer.defer(200, 200, *sReorderingDisplayList);
+        MicroBench::DoNotOptimize(&reorderer);
+    }
+    StopBenchmarkTiming();
+}
+
+BENCHMARK_NO_ARG(BM_OpReorderer_deferAndRender);
+void BM_OpReorderer_deferAndRender::Run(int iters) {
+    TestUtils::runOnRenderThread([this, iters](RenderState& renderState, Caches& caches) {
+        StartBenchmarkTiming();
+        for (int i = 0; i < iters; i++) {
+            OpReorderer reorderer;
+            reorderer.defer(200, 200, *sReorderingDisplayList);
+            MicroBench::DoNotOptimize(&reorderer);
+
+            BakedOpRenderer::Info info(caches, renderState, 200, 200, true);
+            reorderer.replayBakedOps<BakedOpRenderer>(info);
+        }
+        StopBenchmarkTiming();
+    });
+}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 238cf06..f571426 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -323,7 +323,7 @@
     BakedOpRenderer::Info info(Caches::getInstance(), mRenderThread.renderState(),
             frame.width(), frame.height(), mOpaque);
     // TODO: profiler().draw(mCanvas);
-    reorderer.replayBakedOps<BakedOpRenderer>(&info);
+    reorderer.replayBakedOps<BakedOpRenderer>(info);
 
     bool drew = info.didDraw;
 
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index c9b9637..b353ae6 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -137,6 +137,8 @@
     StringCollection extensions(eglQueryString(mEglDisplay, EGL_EXTENSIONS));
     EglExtensions.bufferAge = extensions.has("EGL_EXT_buffer_age");
     EglExtensions.setDamage = extensions.has("EGL_KHR_partial_update");
+    LOG_ALWAYS_FATAL_IF(!extensions.has("EGL_KHR_swap_buffers_with_damage"),
+            "Missing required extension EGL_KHR_swap_buffers_with_damage");
 }
 
 bool EglManager::hasEglContext() {
@@ -322,18 +324,10 @@
     }
 #endif
 
-#ifdef EGL_KHR_swap_buffers_with_damage
-    if (CC_LIKELY(Properties::swapBuffersWithDamage)) {
-        EGLint rects[4];
-        frame.map(screenDirty, rects);
-        eglSwapBuffersWithDamageKHR(mEglDisplay, frame.mSurface, rects,
-                screenDirty.isEmpty() ? 0 : 1);
-    } else {
-        eglSwapBuffers(mEglDisplay, frame.mSurface);
-    }
-#else
-    eglSwapBuffers(mEglDisplay, frame.mSurface);
-#endif
+    EGLint rects[4];
+    frame.map(screenDirty, rects);
+    eglSwapBuffersWithDamageKHR(mEglDisplay, frame.mSurface, rects,
+            screenDirty.isEmpty() ? 0 : 1);
 
     EGLint err = eglGetError();
     if (CC_LIKELY(err == EGL_SUCCESS)) {
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 26aae90..15ccd6a 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -574,12 +574,7 @@
     RenderThread& thread = RenderThread::getInstance();
     void* retval;
     task->setReturnPtr(&retval);
-    Mutex mutex;
-    Condition condition;
-    SignalingRenderTask syncTask(task, &mutex, &condition);
-    AutoMutex _lock(mutex);
-    thread.queue(&syncTask);
-    condition.wait(mutex);
+    thread.queueAndWait(task);
     return retval;
 }
 
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 64075f1..8fcd109 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -312,6 +312,16 @@
     }
 }
 
+void RenderThread::queueAndWait(RenderTask* task) {
+    Mutex mutex;
+    Condition condition;
+    SignalingRenderTask syncTask(task, &mutex, &condition);
+
+    AutoMutex _lock(mutex);
+    queue(&syncTask);
+    condition.wait(mutex);
+}
+
 void RenderThread::queueAtFront(RenderTask* task) {
     AutoMutex _lock(mLock);
     mQueue.queueAtFront(task);
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 8096099..f3444a8 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -39,6 +39,7 @@
 namespace uirenderer {
 
 class RenderState;
+class TestUtils;
 
 namespace renderthread {
 
@@ -76,6 +77,7 @@
     // RenderThread takes complete ownership of tasks that are queued
     // and will delete them after they are run
     ANDROID_API void queue(RenderTask* task);
+    ANDROID_API void queueAndWait(RenderTask* task);
     ANDROID_API void queueAtFront(RenderTask* task);
     void queueAt(RenderTask* task, nsecs_t runAtNs);
     void remove(RenderTask* task);
@@ -101,6 +103,7 @@
     friend class Singleton<RenderThread>;
     friend class DispatchFrameCallbacks;
     friend class RenderProxy;
+    friend class android::uirenderer::TestUtils;
 
     RenderThread();
     virtual ~RenderThread();
diff --git a/libs/hwui/unit_tests/OpReordererTests.cpp b/libs/hwui/unit_tests/OpReordererTests.cpp
index e1249fb..d02f89d 100644
--- a/libs/hwui/unit_tests/OpReordererTests.cpp
+++ b/libs/hwui/unit_tests/OpReordererTests.cpp
@@ -27,26 +27,69 @@
 namespace android {
 namespace uirenderer {
 
-#define UNSUPPORTED_OP(Info, Type) \
-        static void on##Type(Info*, const Type&, const BakedOpState&) { FAIL(); }
+/**
+ * Class that redirects static operation dispatch to virtual methods on a Client class.
+ *
+ * The client is recreated for every op (so data cannot be persisted between operations), but the
+ * virtual dispatch allows for default behaviors to be specified without enumerating each operation
+ * for every test.
+ *
+ * onXXXOp methods fail by default - tests should override ops they expect
+ * startFrame/endFrame do nothing by default - tests should override to intercept
+ */
+template<class CustomClient, class Arg>
+class TestReceiver {
+public:
+#define CLIENT_METHOD(Type) \
+    virtual void on##Type(Arg&, const Type&, const BakedOpState&) { FAIL(); }
+    class Client {
+    public:
+        virtual ~Client() {};
+        MAP_OPS(CLIENT_METHOD)
+
+        virtual void startFrame(Arg& info) {}
+        virtual void endFrame(Arg& info) {}
+    };
+
+#define DISPATCHER_METHOD(Type) \
+    static void on##Type(Arg& arg, const Type& op, const BakedOpState& state) { \
+        CustomClient client; client.on##Type(arg, op, state); \
+    }
+    MAP_OPS(DISPATCHER_METHOD)
+
+    static void startFrame(Arg& info) {
+        CustomClient client;
+        client.startFrame(info);
+    }
+
+    static void endFrame(Arg& info) {
+        CustomClient client;
+        client.endFrame(info);
+    }
+};
 
 class Info {
 public:
     int index = 0;
 };
 
-class SimpleReceiver {
+// Receiver class which will fail if it receives any ops
+class FailReceiver : public TestReceiver<FailReceiver, Info>::Client {};
+
+class SimpleReceiver : public TestReceiver<SimpleReceiver, Info>::Client {
 public:
-    static void onBitmapOp(Info* info, const BitmapOp& op, const BakedOpState& state) {
-        EXPECT_EQ(1, info->index++);
+    void startFrame(Info& info) override {
+        EXPECT_EQ(0, info.index++);
     }
-    static void onRectOp(Info* info, const RectOp& op, const BakedOpState& state) {
-        EXPECT_EQ(0, info->index++);
+    void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
+        EXPECT_EQ(1, info.index++);
     }
-    UNSUPPORTED_OP(Info, RenderNodeOp)
-    UNSUPPORTED_OP(Info, SimpleRectsOp)
-    static void startFrame(Info& info) {}
-    static void endFrame(Info& info) {}
+    void onBitmapOp(Info& info, const BitmapOp& op, const BakedOpState& state) override {
+        EXPECT_EQ(2, info.index++);
+    }
+    void endFrame(Info& info) override {
+        EXPECT_EQ(3, info.index++);
+    }
 };
 TEST(OpReorderer, simple) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
@@ -54,28 +97,39 @@
         canvas.drawRect(0, 0, 100, 200, SkPaint());
         canvas.drawBitmap(bitmap, 10, 10, nullptr);
     });
-
     OpReorderer reorderer;
     reorderer.defer(200, 200, *dl);
 
     Info info;
-    reorderer.replayBakedOps<SimpleReceiver>(&info);
+    reorderer.replayBakedOps<TestReceiver<SimpleReceiver, Info>>(info);
+    EXPECT_EQ(4, info.index); // 2 ops + start + end
+}
+
+
+TEST(OpReorderer, simpleRejection) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+        canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty
+        canvas.drawRect(0, 0, 400, 400, SkPaint());
+        canvas.restore();
+    });
+    OpReorderer reorderer;
+    reorderer.defer(200, 200, *dl);
+
+    Info info;
+    reorderer.replayBakedOps<TestReceiver<FailReceiver, Info>>(info);
 }
 
 
 static int SIMPLE_BATCHING_LOOPS = 5;
-class SimpleBatchingReceiver {
+class SimpleBatchingReceiver : public TestReceiver<SimpleBatchingReceiver, Info>::Client {
 public:
-    static void onBitmapOp(Info* info, const BitmapOp& op, const BakedOpState& state) {
-        EXPECT_TRUE(info->index++ >= SIMPLE_BATCHING_LOOPS);
+    void onBitmapOp(Info& info, const BitmapOp& op, const BakedOpState& state) override {
+        EXPECT_TRUE(info.index++ >= SIMPLE_BATCHING_LOOPS);
     }
-    static void onRectOp(Info* info, const RectOp& op, const BakedOpState& state) {
-        EXPECT_TRUE(info->index++ < SIMPLE_BATCHING_LOOPS);
+    void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
+        EXPECT_TRUE(info.index++ < SIMPLE_BATCHING_LOOPS);
     }
-    UNSUPPORTED_OP(Info, RenderNodeOp)
-    UNSUPPORTED_OP(Info, SimpleRectsOp)
-    static void startFrame(Info& info) {}
-    static void endFrame(Info& info) {}
 };
 TEST(OpReorderer, simpleBatching) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
@@ -96,15 +150,14 @@
     reorderer.defer(200, 200, *dl);
 
     Info info;
-    reorderer.replayBakedOps<SimpleBatchingReceiver>(&info);
+    reorderer.replayBakedOps<TestReceiver<SimpleBatchingReceiver, Info>>(info);
     EXPECT_EQ(2 * SIMPLE_BATCHING_LOOPS, info.index); // 2 x loops ops, because no merging (TODO: force no merging)
 }
 
-class RenderNodeReceiver {
+class RenderNodeReceiver : public TestReceiver<RenderNodeReceiver, Info>::Client {
 public:
-    UNSUPPORTED_OP(Info, BitmapOp)
-    static void onRectOp(Info* info, const RectOp& op, const BakedOpState& state) {
-        switch(info->index++) {
+    void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
+        switch(info.index++) {
         case 0:
             EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clippedBounds);
             EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
@@ -117,10 +170,6 @@
             FAIL();
         }
     }
-    UNSUPPORTED_OP(Info, RenderNodeOp)
-    UNSUPPORTED_OP(Info, SimpleRectsOp)
-    static void startFrame(Info& info) {}
-    static void endFrame(Info& info) {}
 };
 TEST(OpReorderer, renderNode) {
     sp<RenderNode> child = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, [](RecordingCanvas& canvas) {
@@ -151,22 +200,17 @@
     reorderer.defer(SkRect::MakeWH(200, 200), 200, 200, nodes);
 
     Info info;
-    reorderer.replayBakedOps<RenderNodeReceiver>(&info);
+    reorderer.replayBakedOps<TestReceiver<RenderNodeReceiver, Info>>(info);
 }
 
-class ClippedReceiver {
+class ClippedReceiver : public TestReceiver<ClippedReceiver, Info>::Client {
 public:
-    static void onBitmapOp(Info* info, const BitmapOp& op, const BakedOpState& state) {
-        EXPECT_EQ(0, info->index++);
+    void onBitmapOp(Info& info, const BitmapOp& op, const BakedOpState& state) override {
+        EXPECT_EQ(0, info.index++);
         EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
         EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect);
         EXPECT_TRUE(state.computedState.transform.isIdentity());
     }
-    UNSUPPORTED_OP(Info, RectOp)
-    UNSUPPORTED_OP(Info, RenderNodeOp)
-    UNSUPPORTED_OP(Info, SimpleRectsOp)
-    static void startFrame(Info& info) {}
-    static void endFrame(Info& info) {}
 };
 TEST(OpReorderer, clipped) {
     sp<RenderNode> node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RecordingCanvas& canvas) {
@@ -182,8 +226,106 @@
             200, 200, nodes);
 
     Info info;
-    reorderer.replayBakedOps<ClippedReceiver>(&info);
+    reorderer.replayBakedOps<TestReceiver<ClippedReceiver, Info>>(info);
 }
 
+
+class SaveLayerSimpleReceiver : public TestReceiver<SaveLayerSimpleReceiver, Info>::Client {
+public:
+    void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
+        EXPECT_EQ(0, info.index++);
+        EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
+        EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clippedBounds);
+        EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clipRect);
+
+        Matrix4 expectedTransform;
+        expectedTransform.loadTranslate(-10, -10, 0);
+        EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
+    }
+    void onLayerOp(Info& info, const LayerOp& op, const BakedOpState& state) override {
+        EXPECT_EQ(1, info.index++);
+        EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
+        EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clipRect);
+        EXPECT_TRUE(state.computedState.transform.isIdentity());
+    }
+};
+TEST(OpReorderer, saveLayerSimple) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+        canvas.saveLayerAlpha(10, 10, 190, 190, 128, SkCanvas::kClipToLayer_SaveFlag);
+        canvas.drawRect(10, 10, 190, 190, SkPaint());
+        canvas.restore();
+    });
+
+    OpReorderer reorderer;
+    reorderer.defer(200, 200, *dl);
+
+    Info info;
+    reorderer.replayBakedOps<TestReceiver<SaveLayerSimpleReceiver, Info>>(info);
+    EXPECT_EQ(2, info.index);
 }
+
+
+// saveLayer1 {rect1, saveLayer2 { rect2 } } will play back as rect2, rect1, layerOp2, layerOp1
+class SaveLayerNestedReceiver : public TestReceiver<SaveLayerNestedReceiver, Info>::Client {
+public:
+    void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
+        const int index = info.index++;
+        if (index == 0) {
+            EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner rect
+        } else if (index == 1) {
+            EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer rect
+        } else { FAIL(); }
+    }
+    void onLayerOp(Info& info, const LayerOp& op, const BakedOpState& state) override {
+        const int index = info.index++;
+        if (index == 2) {
+            EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner layer
+        } else if (index == 3) {
+            EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer layer
+        } else { FAIL(); }
+    }
+};
+TEST(OpReorderer, saveLayerNested) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(800, 800, [](RecordingCanvas& canvas) {
+        canvas.saveLayerAlpha(0, 0, 800, 800, 128, SkCanvas::kClipToLayer_SaveFlag);
+        {
+            canvas.drawRect(0, 0, 800, 800, SkPaint());
+            canvas.saveLayerAlpha(0, 0, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag);
+            {
+                canvas.drawRect(0, 0, 400, 400, SkPaint());
+            }
+            canvas.restore();
+        }
+        canvas.restore();
+    });
+
+    OpReorderer reorderer;
+    reorderer.defer(800, 800, *dl);
+
+    Info info;
+    reorderer.replayBakedOps<TestReceiver<SaveLayerNestedReceiver, Info>>(info);
+    EXPECT_EQ(4, info.index);
 }
+
+TEST(OpReorderer, saveLayerContentRejection) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+        canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
+        canvas.saveLayerAlpha(200, 200, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag);
+
+        // draw within save layer may still be recorded, but shouldn't be drawn
+        canvas.drawRect(200, 200, 400, 400, SkPaint());
+
+        canvas.restore();
+        canvas.restore();
+    });
+    OpReorderer reorderer;
+    reorderer.defer(200, 200, *dl);
+    Info info;
+
+    // should see no ops, even within the layer, since the layer should be rejected
+    reorderer.replayBakedOps<TestReceiver<FailReceiver, Info>>(info);
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/unit_tests/RecordingCanvasTests.cpp b/libs/hwui/unit_tests/RecordingCanvasTests.cpp
index ce25fc6..c023123 100644
--- a/libs/hwui/unit_tests/RecordingCanvasTests.cpp
+++ b/libs/hwui/unit_tests/RecordingCanvasTests.cpp
@@ -24,11 +24,11 @@
 namespace uirenderer {
 
 static void playbackOps(const DisplayList& displayList,
-        std::function<void(const RecordedOp&)> opReciever) {
+        std::function<void(const RecordedOp&)> opReceiver) {
     for (const DisplayList::Chunk& chunk : displayList.getChunks()) {
         for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
             RecordedOp* op = displayList.getOps()[opIndex];
-            opReciever(*op);
+            opReceiver(*op);
         }
     }
 }
@@ -109,5 +109,123 @@
     ASSERT_EQ(2, count); // two draws observed
 }
 
+TEST(RecordingCanvas, saveLayerSimple) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+        canvas.saveLayerAlpha(10, 20, 190, 180, 128, SkCanvas::kARGB_ClipLayer_SaveFlag);
+        canvas.drawRect(10, 20, 190, 180, SkPaint());
+        canvas.restore();
+    });
+    int count = 0;
+    playbackOps(*dl, [&count](const RecordedOp& op) {
+        Matrix4 expectedMatrix;
+        switch(count++) {
+        case 0:
+            EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId);
+            // TODO: add asserts
+            break;
+        case 1:
+            EXPECT_EQ(RecordedOpId::RectOp, op.opId);
+            EXPECT_EQ(Rect(0, 0, 180, 160), op.localClipRect);
+            EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
+            expectedMatrix.loadTranslate(-10, -20, 0);
+            EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
+            break;
+        case 2:
+            EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
+            // TODO: add asserts
+            break;
+        default:
+            FAIL();
+        }
+    });
+    EXPECT_EQ(3, count);
 }
+
+TEST(RecordingCanvas, saveLayerViewportCrop) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+        // shouldn't matter, since saveLayer will clip to its bounds
+        canvas.clipRect(-1000, -1000, 1000, 1000, SkRegion::kReplace_Op);
+
+        canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::kARGB_ClipLayer_SaveFlag);
+        canvas.drawRect(0, 0, 400, 400, SkPaint());
+        canvas.restore();
+    });
+    int count = 0;
+    playbackOps(*dl, [&count](const RecordedOp& op) {
+        if (count++ == 1) {
+            Matrix4 expectedMatrix;
+            EXPECT_EQ(RecordedOpId::RectOp, op.opId);
+
+            // recorded clip rect should be intersection of
+            // viewport and saveLayer bounds, in layer space
+            EXPECT_EQ(Rect(0, 0, 100, 100), op.localClipRect);
+            EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds);
+            expectedMatrix.loadTranslate(-100, -100, 0);
+            EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
+        }
+    });
+    EXPECT_EQ(3, count);
 }
+
+TEST(RecordingCanvas, saveLayerRotateUnclipped) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+        canvas.translate(100, 100);
+        canvas.rotate(45);
+        canvas.translate(-50, -50);
+
+        canvas.saveLayerAlpha(0, 0, 100, 100, 128, SkCanvas::kARGB_ClipLayer_SaveFlag);
+        canvas.drawRect(0, 0, 100, 100, SkPaint());
+        canvas.restore();
+
+        canvas.restore();
+    });
+    int count = 0;
+    playbackOps(*dl, [&count](const RecordedOp& op) {
+        if (count++ == 1) {
+            Matrix4 expectedMatrix;
+            EXPECT_EQ(RecordedOpId::RectOp, op.opId);
+
+            // recorded rect doesn't see rotate, since recorded relative to saveLayer bounds
+            EXPECT_EQ(Rect(0, 0, 100, 100), op.localClipRect);
+            EXPECT_EQ(Rect(0, 0, 100, 100), op.unmappedBounds);
+            expectedMatrix.loadIdentity();
+            EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
+        }
+    });
+    EXPECT_EQ(3, count);
+}
+
+TEST(RecordingCanvas, saveLayerRotateClipped) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+        canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+        canvas.translate(100, 100);
+        canvas.rotate(45);
+        canvas.translate(-200, -200);
+
+        // area of saveLayer will be clipped to parent viewport, so we ask for 400x400...
+        canvas.saveLayerAlpha(0, 0, 400, 400, 128, SkCanvas::kARGB_ClipLayer_SaveFlag);
+        canvas.drawRect(0, 0, 400, 400, SkPaint());
+        canvas.restore();
+
+        canvas.restore();
+    });
+    int count = 0;
+    playbackOps(*dl, [&count](const RecordedOp& op) {
+        if (count++ == 1) {
+            Matrix4 expectedMatrix;
+            EXPECT_EQ(RecordedOpId::RectOp, op.opId);
+
+            // ...and get about 58.6, 58.6, 341.4 341.4, because the bounds are clipped by
+            // the parent 200x200 viewport, but prior to rotation
+            EXPECT_RECT_APPROX_EQ(Rect(58.57864, 58.57864, 341.42136, 341.42136), op.localClipRect);
+            EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds);
+            expectedMatrix.loadIdentity();
+            EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
+        }
+    });
+    EXPECT_EQ(3, count);
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/unit_tests/TestUtils.h b/libs/hwui/unit_tests/TestUtils.h
index bb58928..99ecc9b 100644
--- a/libs/hwui/unit_tests/TestUtils.h
+++ b/libs/hwui/unit_tests/TestUtils.h
@@ -17,8 +17,11 @@
 #define TEST_UTILS_H
 
 #include <Matrix.h>
-#include <Snapshot.h>
+#include <Rect.h>
 #include <RenderNode.h>
+#include <renderstate/RenderState.h>
+#include <renderthread/RenderThread.h>
+#include <Snapshot.h>
 
 #include <memory>
 
@@ -28,6 +31,12 @@
 #define EXPECT_MATRIX_APPROX_EQ(a, b) \
     EXPECT_TRUE(TestUtils::matricesAreApproxEqual(a, b))
 
+#define EXPECT_RECT_APPROX_EQ(a, b) \
+    EXPECT_TRUE(MathUtils::areEqual(a.left, b.left) \
+            && MathUtils::areEqual(a.top, b.top) \
+            && MathUtils::areEqual(a.right, b.right) \
+            && MathUtils::areEqual(a.bottom, b.bottom));
+
 class TestUtils {
 public:
     static bool matricesAreApproxEqual(const Matrix4& a, const Matrix4& b) {
@@ -48,7 +57,9 @@
 
     static SkBitmap createSkBitmap(int width, int height) {
         SkBitmap bitmap;
-        bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
+        SkImageInfo info = SkImageInfo::MakeUnknown(width, height);
+        bitmap.setInfo(info);
+        bitmap.allocPixels(info);
         return bitmap;
     }
 
@@ -78,6 +89,32 @@
         node->syncProperties();
         node->syncDisplayList();
     }
+
+    typedef std::function<void(RenderState& state, Caches& caches)> RtCallback;
+
+    class TestTask : public renderthread::RenderTask {
+    public:
+        TestTask(RtCallback rtCallback)
+                : rtCallback(rtCallback) {}
+        virtual ~TestTask() {}
+        virtual void run() override {
+            // RenderState only valid once RenderThread is running, so queried here
+            RenderState& renderState = renderthread::RenderThread::getInstance().renderState();
+
+            renderState.onGLContextCreated();
+            rtCallback(renderState, Caches::getInstance());
+            renderState.onGLContextDestroyed();
+        };
+        RtCallback rtCallback;
+    };
+
+    /**
+     * NOTE: requires surfaceflinger to run, otherwise this method will wait indefinitely.
+     */
+    static void runOnRenderThread(RtCallback rtCallback) {
+        TestTask task(rtCallback);
+        renderthread::RenderThread::getInstance().queueAndWait(&task);
+    }
 }; // class TestUtils
 
 } /* namespace uirenderer */
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 0f86bc6..4a1d7e7 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -78,7 +78,7 @@
     mLocked.pointerAlpha = 0.0f; // pointer is initially faded
     mLocked.pointerSprite = mSpriteController->createSprite();
     mLocked.pointerIconChanged = false;
-    mLocked.requestedPointerShape = 0;
+    mLocked.requestedPointerShape = mPolicy->getDefaultPointerIconId();
 
     mLocked.buttonState = 0;
 
@@ -512,7 +512,7 @@
 
     if (mLocked.pointerIconChanged || mLocked.presentationChanged) {
         if (mLocked.presentation == PRESENTATION_POINTER) {
-            if (mLocked.requestedPointerShape == 0) {
+            if (mLocked.requestedPointerShape == mPolicy->getDefaultPointerIconId()) {
                 mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
             } else {
                 std::map<int, SpriteIcon>::const_iterator iter =
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 308ff12..24a1681 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -58,7 +58,8 @@
 
 public:
     virtual void loadPointerResources(PointerResources* outResources) = 0;
-    virtual void loadAdditionalMouseResources(std::map<int, SpriteIcon>* outResources) = 0;
+    virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources) = 0;
+    virtual int32_t getDefaultPointerIconId() = 0;
 };
 
 
diff --git a/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml b/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml
index 90e2b7e..7d7a110 100644
--- a/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml
+++ b/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml
@@ -16,10 +16,6 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item
-        android:state_focused="true"
-        android:color="@color/platform_blue_a200"
-        android:alpha="0.1" />
-    <item
         android:state_activated="true"
         android:color="?android:attr/colorAccent"
         android:alpha="0.1" />
diff --git a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
index d124320..231e110 100644
--- a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
@@ -14,10 +14,16 @@
      limitations under the License.
 -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.documentsui.ListItem xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@drawable/item_doc_list_background">
+    android:background="@drawable/item_doc_list_background"
+    android:orientation="horizontal">
+
+    <View
+        android:id="@+id/focus_indicator"
+        android:layout_width="4dp"
+        android:layout_height="match_parent" />
 
     <LinearLayout
         android:layout_width="match_parent"
@@ -121,4 +127,4 @@
 
     </LinearLayout>
 
-</FrameLayout>
+</com.android.documentsui.ListItem>
diff --git a/packages/DocumentsUI/res/layout/item_doc_list.xml b/packages/DocumentsUI/res/layout/item_doc_list.xml
index c576669..085df35 100644
--- a/packages/DocumentsUI/res/layout/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_list.xml
@@ -14,10 +14,16 @@
      limitations under the License.
 -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.documentsui.DocListItem xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@drawable/item_doc_list_background">
+    android:background="@drawable/item_doc_list_background"
+    android:orientation="horizontal">
+  
+    <View
+        android:id="@+id/focus_indicator"
+        android:layout_width="4dp"
+        android:layout_height="match_parent" />
 
     <LinearLayout
         android:layout_width="match_parent"
@@ -131,4 +137,4 @@
 
     </LinearLayout>
 
-</FrameLayout>
+</com.android.documentsui.DocListItem>
diff --git a/packages/DocumentsUI/res/values-af/config.xml b/packages/DocumentsUI/res/values-af/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-af/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-am/config.xml b/packages/DocumentsUI/res/values-am/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-am/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ar/config.xml b/packages/DocumentsUI/res/values-ar/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ar/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-az-rAZ/config.xml b/packages/DocumentsUI/res/values-az-rAZ/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-az-rAZ/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-bg/config.xml b/packages/DocumentsUI/res/values-bg/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-bg/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-bn-rBD/config.xml b/packages/DocumentsUI/res/values-bn-rBD/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-bn-rBD/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ca/config.xml b/packages/DocumentsUI/res/values-ca/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ca/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-cs/config.xml b/packages/DocumentsUI/res/values-cs/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-cs/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-da/config.xml b/packages/DocumentsUI/res/values-da/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-da/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-de/config.xml b/packages/DocumentsUI/res/values-de/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-de/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-el/config.xml b/packages/DocumentsUI/res/values-el/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-el/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-en-rAU/config.xml b/packages/DocumentsUI/res/values-en-rAU/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-en-rAU/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-en-rGB/config.xml b/packages/DocumentsUI/res/values-en-rGB/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-en-rGB/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-en-rIN/config.xml b/packages/DocumentsUI/res/values-en-rIN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-en-rIN/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-es-rUS/config.xml b/packages/DocumentsUI/res/values-es-rUS/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-es-rUS/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-es/config.xml b/packages/DocumentsUI/res/values-es/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-es/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-et-rEE/config.xml b/packages/DocumentsUI/res/values-et-rEE/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-et-rEE/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-eu-rES/config.xml b/packages/DocumentsUI/res/values-eu-rES/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-eu-rES/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-fa/config.xml b/packages/DocumentsUI/res/values-fa/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-fa/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-fi/config.xml b/packages/DocumentsUI/res/values-fi/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-fi/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-fr-rCA/config.xml b/packages/DocumentsUI/res/values-fr-rCA/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-fr-rCA/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-fr/config.xml b/packages/DocumentsUI/res/values-fr/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-fr/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-gl-rES/config.xml b/packages/DocumentsUI/res/values-gl-rES/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-gl-rES/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-gu-rIN/config.xml b/packages/DocumentsUI/res/values-gu-rIN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-gu-rIN/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-hi/config.xml b/packages/DocumentsUI/res/values-hi/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-hi/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-hr/config.xml b/packages/DocumentsUI/res/values-hr/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-hr/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-hu/config.xml b/packages/DocumentsUI/res/values-hu/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-hu/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-hy-rAM/config.xml b/packages/DocumentsUI/res/values-hy-rAM/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-hy-rAM/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-in/config.xml b/packages/DocumentsUI/res/values-in/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-in/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-is-rIS/config.xml b/packages/DocumentsUI/res/values-is-rIS/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-is-rIS/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-it/config.xml b/packages/DocumentsUI/res/values-it/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-it/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-iw/config.xml b/packages/DocumentsUI/res/values-iw/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-iw/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ja/config.xml b/packages/DocumentsUI/res/values-ja/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ja/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ka-rGE/config.xml b/packages/DocumentsUI/res/values-ka-rGE/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ka-rGE/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-kk-rKZ/config.xml b/packages/DocumentsUI/res/values-kk-rKZ/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-kk-rKZ/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-km-rKH/config.xml b/packages/DocumentsUI/res/values-km-rKH/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-km-rKH/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-kn-rIN/config.xml b/packages/DocumentsUI/res/values-kn-rIN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-kn-rIN/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ko/config.xml b/packages/DocumentsUI/res/values-ko/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ko/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ky-rKG/config.xml b/packages/DocumentsUI/res/values-ky-rKG/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ky-rKG/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-lo-rLA/config.xml b/packages/DocumentsUI/res/values-lo-rLA/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-lo-rLA/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-lt/config.xml b/packages/DocumentsUI/res/values-lt/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-lt/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-lv/config.xml b/packages/DocumentsUI/res/values-lv/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-lv/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-mk-rMK/config.xml b/packages/DocumentsUI/res/values-mk-rMK/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-mk-rMK/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ml-rIN/config.xml b/packages/DocumentsUI/res/values-ml-rIN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ml-rIN/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-mn-rMN/config.xml b/packages/DocumentsUI/res/values-mn-rMN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-mn-rMN/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-mr-rIN/config.xml b/packages/DocumentsUI/res/values-mr-rIN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-mr-rIN/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ms-rMY/config.xml b/packages/DocumentsUI/res/values-ms-rMY/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ms-rMY/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-my-rMM/config.xml b/packages/DocumentsUI/res/values-my-rMM/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-my-rMM/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-nb/config.xml b/packages/DocumentsUI/res/values-nb/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-nb/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ne-rNP/config.xml b/packages/DocumentsUI/res/values-ne-rNP/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ne-rNP/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-nl/config.xml b/packages/DocumentsUI/res/values-nl/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-nl/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-pa-rIN/config.xml b/packages/DocumentsUI/res/values-pa-rIN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-pa-rIN/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-pl/config.xml b/packages/DocumentsUI/res/values-pl/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-pl/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-pt-rBR/config.xml b/packages/DocumentsUI/res/values-pt-rBR/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-pt-rBR/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-pt-rPT/config.xml b/packages/DocumentsUI/res/values-pt-rPT/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-pt-rPT/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-pt/config.xml b/packages/DocumentsUI/res/values-pt/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-pt/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ro/config.xml b/packages/DocumentsUI/res/values-ro/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ro/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ru/config.xml b/packages/DocumentsUI/res/values-ru/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ru/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-si-rLK/config.xml b/packages/DocumentsUI/res/values-si-rLK/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-si-rLK/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-sk/config.xml b/packages/DocumentsUI/res/values-sk/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-sk/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-sl/config.xml b/packages/DocumentsUI/res/values-sl/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-sl/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-sq-rAL/config.xml b/packages/DocumentsUI/res/values-sq-rAL/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-sq-rAL/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-sr/config.xml b/packages/DocumentsUI/res/values-sr/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-sr/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-sv/config.xml b/packages/DocumentsUI/res/values-sv/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-sv/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-sw/config.xml b/packages/DocumentsUI/res/values-sw/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-sw/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ta-rIN/config.xml b/packages/DocumentsUI/res/values-ta-rIN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ta-rIN/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-te-rIN/config.xml b/packages/DocumentsUI/res/values-te-rIN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-te-rIN/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-th/config.xml b/packages/DocumentsUI/res/values-th/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-th/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-tl/config.xml b/packages/DocumentsUI/res/values-tl/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-tl/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-tr/config.xml b/packages/DocumentsUI/res/values-tr/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-tr/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-uk/config.xml b/packages/DocumentsUI/res/values-uk/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-uk/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ur-rPK/config.xml b/packages/DocumentsUI/res/values-ur-rPK/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-ur-rPK/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-uz-rUZ/config.xml b/packages/DocumentsUI/res/values-uz-rUZ/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-uz-rUZ/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-vi/config.xml b/packages/DocumentsUI/res/values-vi/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-vi/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-zh-rCN/config.xml b/packages/DocumentsUI/res/values-zh-rCN/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-zh-rCN/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-zh-rHK/config.xml b/packages/DocumentsUI/res/values-zh-rHK/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-zh-rHK/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-zh-rTW/config.xml b/packages/DocumentsUI/res/values-zh-rTW/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-zh-rTW/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-zu/config.xml b/packages/DocumentsUI/res/values-zu/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-zu/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="trusted_quick_viewer_package" msgid="3354383993907861267"></string>
+</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 7662689..45a8907 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -68,6 +68,7 @@
 import android.support.v7.widget.RecyclerView.OnItemTouchListener;
 import android.support.v7.widget.RecyclerView.RecyclerListener;
 import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.support.v7.widget.SimpleItemAnimator;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.text.format.Formatter;
@@ -79,12 +80,12 @@
 import android.view.ActionMode;
 import android.view.DragEvent;
 import android.view.GestureDetector;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.View.OnLayoutChangeListener;
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.widget.ImageView;
@@ -97,10 +98,12 @@
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.DocumentStack;
 import com.android.documentsui.model.RootInfo;
+import com.android.internal.annotations.GuardedBy;
 
 import com.google.common.collect.Lists;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -134,6 +137,7 @@
     private Model mModel;
     private MultiSelectManager mSelectionManager;
     private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener();
+    private ItemClickListener mItemClickListener = new ItemClickListener();
 
     private View mEmptyView;
     private RecyclerView mRecView;
@@ -238,7 +242,7 @@
         // TODO: Rather than update columns on layout changes, push this
         // code (or something like it) into GridLayoutManager.
         mRecView.addOnLayoutChangeListener(
-                new OnLayoutChangeListener() {
+                new View.OnLayoutChangeListener() {
 
                     @Override
                     public void onLayoutChange(
@@ -251,6 +255,9 @@
                     }
                 });
 
+        // TODO: Restore transition animations.  See b/24802917.
+        ((SimpleItemAnimator) mRecView.getItemAnimator()).setSupportsChangeAnimations(false);
+
         // TODO: Add a divider between views (which might use RecyclerView.ItemDecoration).
         if (DEBUG_ENABLE_DND) {
             setupDragAndDropOnDirectoryView(mRecView);
@@ -450,7 +457,10 @@
     }
 
     private boolean onSingleTapUp(MotionEvent e) {
-        if (Events.isTouchEvent(e) && mSelectionManager.getSelection().isEmpty()) {
+        // Only respond to touch events.  Single-click mouse events are selection events and are
+        // handled by the selection manager.  Tap events that occur while the selection manager is
+        // active are also selection events.
+        if (Events.isTouchEvent(e) && !mSelectionManager.hasSelection()) {
             int position = getEventAdapterPosition(e);
             if (position != RecyclerView.NO_POSITION) {
                 return handleViewItem(position);
@@ -825,7 +835,7 @@
         Snackbars.makeSnackbar(activity, message, Snackbar.LENGTH_LONG)
                 .setAction(
                         R.string.undo,
-                        new android.view.View.OnClickListener() {
+                        new View.OnClickListener() {
                             @Override
                             public void onClick(View view) {}
                         })
@@ -889,10 +899,16 @@
     // Provide a reference to the views for each data item
     // Complex data items may need more than one view per item, and
     // you provide access to all the views for a data item in a view holder
-    private static final class DocumentHolder extends RecyclerView.ViewHolder {
+    private static final class DocumentHolder
+            extends RecyclerView.ViewHolder
+            implements View.OnKeyListener
+    {
         // each data item is just a string in this case
         public View view;
         public String docId;  // The stable document id.
+        private ClickListener mClickListener;
+        private View.OnKeyListener mKeyListener;
+
         public DocumentHolder(View view) {
             super(view);
             this.view = view;
@@ -900,6 +916,38 @@
             // So we set it here.  Note that touch mode focus is a separate issue - see
             // View.setFocusableInTouchMode and View.isInTouchMode for more info.
             this.view.setFocusable(true);
+            this.view.setOnKeyListener(this);
+        }
+
+        @Override
+        public boolean onKey(View v, int keyCode, KeyEvent event) {
+            // Intercept enter key-up events, and treat them as clicks.  Forward other events.
+            if (event.getAction() == KeyEvent.ACTION_UP &&
+                    keyCode == KeyEvent.KEYCODE_ENTER) {
+                if (mClickListener != null) {
+                    mClickListener.onClick(this);
+                }
+                return true;
+            } else if (mKeyListener != null) {
+                return mKeyListener.onKey(v, keyCode, event);
+            }
+            return false;
+        }
+
+        public void addClickListener(ClickListener listener) {
+            // Just handle one for now; switch to a list if necessary.
+            checkState(mClickListener == null);
+            mClickListener = listener;
+        }
+
+        public void addOnKeyListener(View.OnKeyListener listener) {
+            // Just handle one for now; switch to a list if necessary.
+            checkState(mKeyListener == null);
+            mKeyListener = listener;
+        }
+
+        interface ClickListener {
+            public void onClick(DocumentHolder doc);
         }
     }
 
@@ -952,10 +1000,11 @@
                 default:
                     throw new IllegalStateException("Unsupported layout mode.");
             }
-            // Key event bubbling doesn't work properly, so instead of setting one key listener on
-            // the RecyclerView, we have to set it on each Item.  See b/24865023.
-            item.setOnKeyListener(mSelectionManager);
-            return new DocumentHolder(item);
+
+            DocumentHolder holder = new DocumentHolder(item);
+            holder.addClickListener(mItemClickListener);
+            holder.addOnKeyListener(mSelectionManager);
+            return holder;
         }
 
         @Override
@@ -1696,6 +1745,9 @@
         private Context mContext;
         private int mCursorCount;
         private boolean mIsLoading;
+        @GuardedBy("mPendingDelete")
+        private Boolean mPendingDelete = false;
+        @GuardedBy("mPendingDelete")
         private SparseBooleanArray mMarkedForDeletion = new SparseBooleanArray();
         private UpdateListener mUpdateListener;
         @Nullable private Cursor mCursor;
@@ -1740,35 +1792,39 @@
         }
 
         int getItemCount() {
-            return mCursorCount - mMarkedForDeletion.size();
+            synchronized(mPendingDelete) {
+                return mCursorCount - mMarkedForDeletion.size();
+            }
         }
 
         Cursor getItem(int position) {
-            // Items marked for deletion are masked out of the UI.  To do this, for every marked
-            // item whose position is less than the requested item position, advance the requested
-            // position by 1.
-            final int originalPos = position;
-            final int size = mMarkedForDeletion.size();
-            for (int i = 0; i < size; ++i) {
-                // It'd be more concise, but less efficient, to iterate over positions while calling
-                // mMarkedForDeletion.get.  Instead, iterate over deleted entries.
-                if (mMarkedForDeletion.keyAt(i) <= position && mMarkedForDeletion.valueAt(i)) {
-                    ++position;
+            synchronized(mPendingDelete) {
+                // Items marked for deletion are masked out of the UI.  To do this, for every marked
+                // item whose position is less than the requested item position, advance the requested
+                // position by 1.
+                final int originalPos = position;
+                final int size = mMarkedForDeletion.size();
+                for (int i = 0; i < size; ++i) {
+                    // It'd be more concise, but less efficient, to iterate over positions while calling
+                    // mMarkedForDeletion.get.  Instead, iterate over deleted entries.
+                    if (mMarkedForDeletion.keyAt(i) <= position && mMarkedForDeletion.valueAt(i)) {
+                        ++position;
+                    }
                 }
-            }
 
-            if (DEBUG && position != originalPos) {
-                Log.d(TAG, "Item position adjusted for deletion.  Original: " + originalPos
-                        + "  Adjusted: " + position);
-            }
+                if (DEBUG && position != originalPos) {
+                    Log.d(TAG, "Item position adjusted for deletion.  Original: " + originalPos
+                            + "  Adjusted: " + position);
+                }
 
-            if (position >= mCursorCount) {
-                throw new IndexOutOfBoundsException("Attempt to retrieve " + position + " of " +
-                        mCursorCount + " items");
-            }
+                if (position >= mCursorCount) {
+                    throw new IndexOutOfBoundsException("Attempt to retrieve " + position + " of " +
+                            mCursorCount + " items");
+                }
 
-            mCursor.moveToPosition(position);
-            return mCursor;
+                mCursor.moveToPosition(position);
+                return mCursor;
+            }
         }
 
         private boolean isEmpty() {
@@ -1801,17 +1857,19 @@
         }
 
         List<DocumentInfo> getDocumentsMarkedForDeletion() {
-            final int size = mMarkedForDeletion.size();
-            List<DocumentInfo> docs =  new ArrayList<>(size);
+            synchronized (mPendingDelete) {
+                final int size = mMarkedForDeletion.size();
+                List<DocumentInfo> docs =  new ArrayList<>(size);
 
-            for (int i = 0; i < size; ++i) {
-                final int position = mMarkedForDeletion.keyAt(i);
-                checkState(position < mCursorCount);
-                mCursor.moveToPosition(position);
-                final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(mCursor);
-                docs.add(doc);
+                for (int i = 0; i < size; ++i) {
+                    final int position = mMarkedForDeletion.keyAt(i);
+                    checkState(position < mCursorCount);
+                    mCursor.moveToPosition(position);
+                    final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(mCursor);
+                    docs.add(doc);
+                }
+                return docs;
             }
-            return docs;
         }
 
         /**
@@ -1822,17 +1880,23 @@
          * @param selected A selection representing the files to delete.
          */
         void markForDeletion(Selection selected) {
-            // Only one deletion operation at a time.
-            checkState(mMarkedForDeletion.size() == 0);
-            // There should never be more to delete than what exists.
-            checkState(mCursorCount >= selected.size());
+            synchronized (mPendingDelete) {
+                mPendingDelete = true;
+                // Only one deletion operation at a time.
+                checkState(mMarkedForDeletion.size() == 0);
+                // There should never be more to delete than what exists.
+                checkState(mCursorCount >= selected.size());
 
-            final int size = selected.size();
-            for (int i = 0; i < size; ++i) {
-                int position = selected.get(i);
-                if (DEBUG) Log.d(TAG, "Marked position " + position + " for deletion");
-                mMarkedForDeletion.append(position, true);
-                mViewAdapter.notifyItemRemoved(position);
+                int[] positions = selected.getAll();
+                Arrays.sort(positions);
+
+                // Walk backwards through the set, since we're removing positions.
+                // Otherwise, positions would change after the first modification.
+                for (int p = positions.length - 1; p >= 0; p--) {
+                    mMarkedForDeletion.append(positions[p], true);
+                    mViewAdapter.notifyItemRemoved(positions[p]);
+                    if (DEBUG) Log.d(TAG, "Scheduled " + positions[p] + " for delete.");
+                }
             }
         }
 
@@ -1841,17 +1905,24 @@
          * unmarked, and restored in the UI.  See {@link #markForDeletion(Selection)}.
          */
         void undoDeletion() {
-            // Iterate over deleted items, temporarily marking them false in the deletion list, and
-            // re-adding them to the UI.
-            final int size = mMarkedForDeletion.size();
-            for (int i = 0; i < size; ++i) {
-                final int position = mMarkedForDeletion.keyAt(i);
-                mMarkedForDeletion.put(position, false);
-                mViewAdapter.notifyItemInserted(position);
+            synchronized (mPendingDelete) {
+                // Iterate over deleted items, temporarily marking them false in the deletion list, and
+                // re-adding them to the UI.
+                final int size = mMarkedForDeletion.size();
+                for (int i = 0; i < size; ++i) {
+                    final int position = mMarkedForDeletion.keyAt(i);
+                    mMarkedForDeletion.put(position, false);
+                    mViewAdapter.notifyItemInserted(position);
+                }
+                resetDeleteData();
             }
+        }
 
-            // Then, clear the deletion list.
-            mMarkedForDeletion.clear();
+        private void resetDeleteData() {
+            synchronized (mPendingDelete) {
+                mPendingDelete = false;
+                mMarkedForDeletion.clear();
+            }
         }
 
         /**
@@ -1862,9 +1933,16 @@
          * snackbars) for errors, info, etc.
          */
         void finalizeDeletion(DeletionListener listener) {
-            final ContentResolver resolver = mContext.getContentResolver();
-            DeleteFilesTask task = new DeleteFilesTask(resolver, listener);
-            task.execute();
+            synchronized (mPendingDelete) {
+                if (mPendingDelete) {
+                    // Necessary to avoid b/25072545. Even when that's resolved, this
+                    // is a nice safe thing to day.
+                    mPendingDelete = false;
+                    final ContentResolver resolver = mContext.getContentResolver();
+                    DeleteFilesTask task = new DeleteFilesTask(resolver, listener);
+                    task.execute();
+                }
+            }
         }
 
         /**
@@ -1921,7 +1999,7 @@
                 } else {
                     if (DEBUG) Log.d(TAG, "Deletion task completed successfully.");
                 }
-                mMarkedForDeletion.clear();
+                resetDeleteData();
 
                 mListener.onCompletion();
             }
@@ -1957,6 +2035,18 @@
         }
     }
 
+    private class ItemClickListener implements DocumentHolder.ClickListener {
+        @Override
+        public void onClick(DocumentHolder doc) {
+            final int position = doc.getAdapterPosition();
+            if (mSelectionManager.hasSelection()) {
+                mSelectionManager.toggleSelection(position);
+            } else {
+                handleViewItem(position);
+            }
+        }
+    }
+
     private class ModelUpdateListener extends Model.UpdateListener {
         @Override
         public void onModelUpdate(Model model) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ListItem.java b/packages/DocumentsUI/src/com/android/documentsui/ListItem.java
new file mode 100644
index 0000000..5c40f1b
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/ListItem.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 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.documentsui;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.LinearLayout;
+
+/**
+ * Layout for a single item in List mode.  This class overrides the default focus listener in order
+ * to light up a focus indicator when it is focused.
+ */
+public class ListItem extends LinearLayout
+{
+    public ListItem(Context context) {
+        super(context);
+    }
+
+    public ListItem(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+        View indicator = findViewById(R.id.focus_indicator);
+        if (gainFocus) {
+            TypedValue color = new TypedValue();
+            getContext().getTheme().resolveAttribute(android.R.attr.colorAccent, color, true);
+            indicator.setBackgroundColor(color.data);
+        } else {
+            indicator.setBackgroundColor(android.R.color.transparent);
+        }
+        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
index 87c037d..ef53d53 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
@@ -183,6 +183,10 @@
         mCallbacks.add(callback);
     }
 
+    public boolean hasSelection() {
+        return !mSelection.isEmpty();
+    }
+
     /**
      * Returns a Selection object that provides a live view
      * on the current selection.
@@ -217,7 +221,7 @@
      */
     @VisibleForTesting
     public boolean setItemSelected(int position, boolean selected) {
-        if (mSingleSelect && !mSelection.isEmpty()) {
+        if (mSingleSelect && hasSelection()) {
             clearSelectionQuietly();
         }
         return setItemsSelected(position, 1, selected);
@@ -263,7 +267,7 @@
     private void clearSelectionQuietly() {
         mRanger = null;
 
-        if (mSelection.isEmpty()) {
+        if (!hasSelection()) {
             return;
         }
         if (mIntermediateSelection == null) {
@@ -292,7 +296,7 @@
     @VisibleForTesting
     boolean onSingleTapUp(InputEvent input) {
         if (DEBUG) Log.d(TAG, "Processing tap event.");
-        if (mSelection.isEmpty()) {
+        if (!hasSelection()) {
             // if this is a mouse click on an item, start selection mode.
             // TODO:  && input.isPrimaryButtonPressed(), but it is returning false.
             if (input.isOverItem() && input.isMouseEvent()) {
@@ -335,7 +339,7 @@
      *
      * @param position
      */
-    private void toggleSelection(int position) {
+    void toggleSelection(int position) {
         // Position may be special "no position" during certain
         // transitional phases. If so, skip handling of the event.
         if (position == RecyclerView.NO_POSITION) {
@@ -351,7 +355,7 @@
             if (!canSelect) {
                 return;
             }
-            if (mSingleSelect && !mSelection.isEmpty()) {
+            if (mSingleSelect && hasSelection()) {
                 clearSelectionQuietly();
             }
 
@@ -398,7 +402,7 @@
             if (selected) {
                 boolean canSelect = notifyBeforeItemStateChange(i, true);
                 if (canSelect) {
-                    if (mSingleSelect && !mSelection.isEmpty()) {
+                    if (mSingleSelect && hasSelection()) {
                         clearSelectionQuietly();
                     }
                     selectAndNotify(i);
@@ -597,6 +601,14 @@
             mTotalSelection = new SparseBooleanArray();
         }
 
+        @VisibleForTesting
+        public Selection(int... positions) {
+            this();
+            for (int i = 0; i < positions.length; i++) {
+                add(positions[i]);
+            }
+        }
+
         /**
          * @param position
          * @return true if the position is currently selected.
@@ -620,6 +632,18 @@
         }
 
         /**
+         * Returns an unordered array of selected positions.
+         */
+        public int[] getAll() {
+            final int size = size();
+            int[] positions = new int[size];
+            for (int i = 0; i < size; i++) {
+                positions[i] = get(i);
+            }
+            return positions;
+        }
+
+        /**
          * @return size of the selection.
          */
         public int size() {
@@ -1959,7 +1983,11 @@
             }
             if (searchDir != -1) {
                 View targetView = view.focusSearch(searchDir);
-                target = mEnvironment.getAdapterPositionForChildView(targetView);
+                // TargetView can be null, for example, if the user pressed <down> at the bottom of
+                // the list.
+                if (targetView != null) {
+                    target = mEnvironment.getAdapterPositionForChildView(targetView);
+                }
             }
         }
 
@@ -1972,18 +2000,13 @@
         mEnvironment.focusItem(target);
 
         if (event.isShiftPressed()) {
-            if (mSelection.isEmpty()) {
+            if (!hasSelection()) {
                 // If there is no selection, start a selection when the user presses shift-arrow.
                 toggleSelection(mEnvironment.getAdapterPositionForChildView(view));
-            } else {
-                // Deal with b/24802917 (selected items can't be focused) by adjusting the
-                // selection sorted the focused item isn't in the selection.
-                target -= Integer.signum(target - mRanger.mBegin);
-                mRanger.snapSelection(target);
             }
-        } else if (!event.isShiftPressed() && !mSelection.isEmpty()) {
-            // If there is a selection, clear it if the user presses arrow with no shift.
-            clearSelection();
+
+            mRanger.snapSelection(target);
+            notifySelectionChanged();
         }
 
         return true;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
index c2b64fb..4bd6ae6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
@@ -36,7 +36,7 @@
 import android.util.Log;
 
 import com.android.documentsui.model.RootInfo;
-
+import com.android.internal.annotations.GuardedBy;
 import com.google.common.util.concurrent.AbstractFuture;
 
 import libcore.io.IoUtils;
@@ -79,6 +79,7 @@
     private final RootsCache mRoots;
     private final State mState;
 
+    @GuardedBy("mTasks")
     private final HashMap<RootInfo, RecentTask> mTasks = new HashMap<>();
 
     private final int mSortOrder = State.SORT_ORDER_LAST_MODIFIED;
@@ -165,6 +166,12 @@
 
     @Override
     public DirectoryResult loadInBackground() {
+        synchronized (mTasks) {
+            return loadInBackgroundLocked();
+        }
+    }
+
+    private DirectoryResult loadInBackgroundLocked() {
         if (mFirstPassLatch == null) {
             // First time through we kick off all the recent tasks, and wait
             // around to see if everyone finishes quickly.
@@ -302,8 +309,10 @@
         // Ensure the loader is stopped
         onStopLoading();
 
-        for (RecentTask task : mTasks.values()) {
-            IoUtils.closeQuietly(task);
+        synchronized (mTasks) {
+            for (RecentTask task : mTasks.values()) {
+                IoUtils.closeQuietly(task);
+            }
         }
 
         IoUtils.closeQuietly(mResult);
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
index ace9e27..36d880a 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
@@ -32,10 +32,6 @@
 import com.android.documentsui.model.DocumentInfo;
 
 import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-
 
 public class DirectoryFragmentModelTest extends AndroidTestCase {
 
@@ -117,15 +113,6 @@
         assertEquals("0", docs.get(0).documentId);
         assertEquals("1", docs.get(1).documentId);
         assertEquals("4", docs.get(2).documentId);
-
-        TestDeletionListener testListener = new TestDeletionListener();
-        model.finalizeDeletion(testListener);
-        testListener.waitForDone();
-
-        docs = getDocumentInfo(0, 1, 2);
-        assertEquals("0", docs.get(0).documentId);
-        assertEquals("1", docs.get(1).documentId);
-        assertEquals("2", docs.get(2).documentId);
     }
 
     // Tests that Model.getItem returns the right items after a deletion is undone.
@@ -149,20 +136,12 @@
         };
     }
 
-    private void delete(int... items) {
-        Selection sel = new Selection();
-        for (int item: items) {
-            sel.add(item);
-        }
-        model.markForDeletion(sel);
+    private void delete(int... positions) {
+        model.markForDeletion(new Selection(positions));
     }
 
-    private List<DocumentInfo> getDocumentInfo(int... items) {
-        Selection sel = new Selection();
-        for (int item: items) {
-            sel.add(item);
-        }
-        return model.getDocuments(sel);
+    private List<DocumentInfo> getDocumentInfo(int... positions) {
+        return model.getDocuments(new Selection(positions));
     }
 
     private static class DummyListener extends Model.UpdateListener {
@@ -177,20 +156,4 @@
             return null;
         }
     }
-
-    private static class TestDeletionListener extends Model.DeletionListener {
-        final CountDownLatch mSignal = new CountDownLatch(1);
-
-        @Override
-        public void onCompletion() {
-            mSignal.countDown();
-        }
-
-        public void waitForDone() {
-            try {
-                boolean timeout = mSignal.await(10, TimeUnit.SECONDS);
-                assertTrue("Timed out waiting for deletion completion", timeout);
-            } catch (InterruptedException e) {}
-        }
-    }
 }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/CursorHelper.java b/packages/MtpDocumentsProvider/src/com/android/mtp/CursorHelper.java
index cc510a9..9e874bd 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/CursorHelper.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/CursorHelper.java
@@ -18,6 +18,7 @@
 
 import android.content.res.Resources;
 import android.database.MatrixCursor;
+import android.media.MediaFile;
 import android.mtp.MtpConstants;
 import android.mtp.MtpObjectInfo;
 import android.provider.DocumentsContract;
@@ -70,30 +71,18 @@
     }
 
     static String formatTypeToMimeType(int format) {
-        // TODO: Add complete list of mime types.
-        switch (format) {
-            case MtpConstants.FORMAT_ASSOCIATION:
-                return DocumentsContract.Document.MIME_TYPE_DIR;
-            case MtpConstants.FORMAT_MP3:
-                return "audio/mp3";
-            case MtpConstants.FORMAT_EXIF_JPEG:
-                return "image/jpeg";
-            default:
-                return "application/octet-stream";
+        if (format == MtpConstants.FORMAT_ASSOCIATION) {
+            return DocumentsContract.Document.MIME_TYPE_DIR;
+        } else {
+            return MediaFile.getMimeTypeForFormatCode(format);
         }
     }
 
-    static int mimeTypeToFormatType(String mimeType) {
-        // TODO: Add complete list of mime types.
-        switch (mimeType.toLowerCase()) {
-            case Document.MIME_TYPE_DIR:
-                return MtpConstants.FORMAT_ASSOCIATION;
-            case "audio/mp3":
-                return MtpConstants.FORMAT_MP3;
-            case "image/jpeg":
-                return MtpConstants.FORMAT_EXIF_JPEG;
-            default:
-                return MtpConstants.FORMAT_UNDEFINED;
+    static int mimeTypeToFormatType(String fileName, String mimeType) {
+        if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+            return MtpConstants.FORMAT_ASSOCIATION;
+        } else {
+            return MediaFile.getFormatCode(fileName, mimeType);
         }
     }
 }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 9d5850b..7883e61 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -242,7 +242,7 @@
                     new MtpObjectInfo.Builder()
                             .setStorageId(parentId.mStorageId)
                             .setParent(parentId.mObjectHandle)
-                            .setFormat(CursorHelper.mimeTypeToFormatType(mimeType))
+                            .setFormat(CursorHelper.mimeTypeToFormatType(displayName, mimeType))
                             .setName(displayName)
                             .build(), pipe[1]);
             final String documentId = new Identifier(parentId.mDeviceId, parentId.mStorageId,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
index 0380e21..f935f31 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -19,6 +19,7 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
+import android.bluetooth.le.BluetoothLeScanner;
 import android.content.Context;
 import android.os.ParcelUuid;
 import android.util.Log;
@@ -106,6 +107,10 @@
         return mAdapter.getScanMode();
     }
 
+    public BluetoothLeScanner getBluetoothLeScanner() {
+        return mAdapter.getBluetoothLeScanner();
+    }
+
     public int getState() {
         return mAdapter.getState();
     }
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 314b3c4..0cc2a67 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -3,7 +3,7 @@
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-proto-files-under,src) \
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-proto-files-under,src) $(call all-Iaidl-files-under, src) \
     src/com/android/systemui/EventLogTags.logtags
 
 LOCAL_STATIC_JAVA_LIBRARIES := Keyguard
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 8fc7ad0..5d622a0 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -127,6 +127,9 @@
     <!-- Assist -->
     <uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" />
 
+    <!-- Listen for keyboard attachment / detachment -->
+    <uses-permission android:name="android.permission.TABLET_MODE" />
+
     <!-- Self permission for internal broadcasts. -->
     <permission android:name="com.android.systemui.permission.SELF"
             android:protectionLevel="signature" />
@@ -196,6 +199,11 @@
                     android:value="com.android.settings.category.system" />
         </activity>
 
+        <!-- Service used by secondary users to register themselves with the system user. -->
+        <service android:name=".recents.RecentsSystemUserService"
+            android:exported="false"
+            android:permission="com.android.systemui.permission.SELF" />
+
         <!-- Alternate Recents -->
         <activity android:name=".recents.RecentsActivity"
                   android:label="@string/accessibility_desc_recent_apps"
@@ -212,17 +220,6 @@
             </intent-filter>
         </activity>
 
-        <receiver android:name=".recents.RecentsUserEventProxyReceiver"
-                  android:exported="false">
-            <intent-filter>
-                <action android:name="com.android.systemui.recents.action.SHOW_RECENTS_FOR_USER" />
-                <action android:name="com.android.systemui.recents.action.HIDE_RECENTS_FOR_USER" />
-                <action android:name="com.android.systemui.recents.action.TOGGLE_RECENTS_FOR_USER" />
-                <action android:name="com.android.systemui.recents.action.PRELOAD_RECENTS_FOR_USER" />
-                <action android:name="com.android.systemui.recents.action.CONFIG_CHANGED_FOR_USER" />
-            </intent-filter>
-        </receiver>
-
         <!-- Callback for dismissing screenshot notification after a share target is picked -->
         <receiver android:name=".screenshot.GlobalScreenshot$TargetChosenReceiver"
                   android:process=":screenshot"
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index 59fed5b..f430fa5 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -19,14 +19,13 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
-    android:paddingBottom="@dimen/navigation_bar_size"
     android:background="@drawable/qs_customizer_background"
     android:gravity="center_horizontal">
 
     <FrameLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:background="?android:attr/colorPrimary">
+        android:background="@drawable/notification_header_bg">
 
         <LinearLayout
             android:id="@+id/drag_buttons"
@@ -72,8 +71,8 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:navigationContentDescription="@*android:string/action_bar_up_description"
-            style="?android:attr/toolbarStyle"
-            android:background="?android:attr/colorPrimary" />
+            android:background="@drawable/notification_header_bg"
+            style="?android:attr/toolbarStyle" />
     </FrameLayout>
 
     <com.android.systemui.tuner.AutoScrollView
@@ -105,4 +104,10 @@
         android:elevation="@dimen/fab_elevation"
         android:background="@drawable/fab_background" />
 
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/navigation_bar_size"
+        android:layout_gravity="bottom"
+        android:background="#ff000000" />
+
 </com.android.systemui.qs.customize.QSCustomizer>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 0b8da83..6187070 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-** Copyright 2015, The Android Open Source Project
+** Copyright 2012, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License"); 
 ** you may not use this file except in compliance with the License. 
@@ -32,55 +32,21 @@
     android:focusable="true"
     >
 
+    <com.android.systemui.qs.QuickQSPanel
+        android:id="@+id/quick_qs_panel"
+        android:background="#0000"
+        android:layout_width="142dp"
+        android:layout_height="match_parent"
+        android:layout_alignParentEnd="true" />
+
     <LinearLayout
         android:id="@+id/expanded_group"
-        android:layout_width="match_parent"
+        android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:paddingEnd="12dp"
-        android:orientation="horizontal">
-
-        <com.android.systemui.statusbar.AlphaOptimizedButton android:id="@+id/alarm_status"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginBottom="4dp"
-            android:drawablePadding="6dp"
-            android:drawableStart="@drawable/ic_access_alarms_small"
-            android:textColor="#64ffffff"
-            android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
-            android:paddingEnd="6dp"
-            android:paddingStart="6dp"
-            android:paddingTop="16dp"
-            android:paddingBottom="16dp"
-            android:background="?android:attr/selectableItemBackground"
-            android:visibility="gone"
-            />
-
-        <com.android.systemui.statusbar.policy.DateView android:id="@+id/date"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="16dp"
-            android:singleLine="true"
-            android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
-            android:layout_below="@id/clock"
-            systemui:datePattern="EEE"
-            android:gravity="center_vertical"
-            android:textSize="20sp"
-            android:paddingTop="16dp"
-            android:layout_marginBottom="@dimen/clock_collapsed_bottom_margin" />
-
-        <include layout="@layout/split_clock_view"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_marginStart="16dp"
-            android:layout_marginTop="16dp"
-            android:id="@+id/clock"
-            />
-
-        <Space
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1" />
-
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:orientation="horizontal"
+        android:layout_alignParentEnd="true">
         <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
             android:id="@+id/settings_button_container"
             android:layout_width="48dp"
@@ -105,8 +71,57 @@
                 android:src="@drawable/tuner" />
 
         </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+
+        <ImageView
+            android:layout_width="48dp"
+            android:layout_height="match_parent"
+            android:src="@drawable/ic_expand_less"
+            android:tint="@android:color/white" />
     </LinearLayout>
 
+    <FrameLayout
+        android:id="@+id/date_group"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="@dimen/clock_collapsed_bottom_margin"
+        android:layout_alignParentBottom="true">
+        <com.android.systemui.statusbar.policy.DateView android:id="@+id/date_collapsed"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="16dp"
+            android:singleLine="true"
+            android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
+            android:layout_below="@id/clock"
+            systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm"
+            />
+    </FrameLayout>
+
+    <include layout="@layout/split_clock_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dp"
+        android:layout_above="@id/date_group"
+        android:id="@+id/clock"
+        />
+
+    <com.android.systemui.statusbar.AlphaOptimizedButton android:id="@+id/alarm_status"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_toEndOf="@id/date_group"
+        android:layout_marginBottom="4dp"
+        android:drawablePadding="6dp"
+        android:drawableStart="@drawable/ic_access_alarms_small"
+        android:textColor="#64ffffff"
+        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
+        android:paddingEnd="6dp"
+        android:paddingStart="6dp"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp"
+        android:background="?android:attr/selectableItemBackground"
+        android:visibility="gone"
+        />
+
     <include
         android:id="@+id/qs_detail_header"
         layout="@layout/qs_detail_header"
@@ -115,12 +130,6 @@
         android:layout_alignParentBottom="true"
         />
 
-    <com.android.systemui.qs.QuickQSPanel
-        android:id="@+id/quick_qs_panel"
-        android:background="#0000"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" />
-
     <com.android.systemui.statusbar.AlphaOptimizedImageView
         android:id="@+id/qs_detail_header_progress"
         android:src="@drawable/indeterminate_anim"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 4d07cdb..f50abf7 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Herrangskik Kitsinstellings"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Wys helderheid in Kitsinstellings"</string>
     <string name="experimental" msgid="6198182315536726162">"Eksperimenteel"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index c1180b6..e507e64 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"ፈጣን ቅንብሮችን ዳግም ያደራጁ"</string>
     <string name="show_brightness" msgid="6613930842805942519">"በፈጣን ቅንብሮች ውስጥ ብሩህነትን አሳይ"</string>
     <string name="experimental" msgid="6198182315536726162">"የሙከራ"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index fc2941f..1098af1 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -444,4 +444,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"إعادة ترتيب الإعدادات السريعة"</string>
     <string name="show_brightness" msgid="6613930842805942519">"عرض السطوع في الإعدادات السريعة"</string>
     <string name="experimental" msgid="6198182315536726162">"إعدادات تجريبية"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-az-rAZ/strings.xml b/packages/SystemUI/res/values-az-rAZ/strings.xml
index 04d680b..4b5309c 100644
--- a/packages/SystemUI/res/values-az-rAZ/strings.xml
+++ b/packages/SystemUI/res/values-az-rAZ/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Sürətli Ayarları yenidən tənzimləyin"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Sürətli ayarlarda parlaqlılığı göstərin"</string>
     <string name="experimental" msgid="6198182315536726162">"Eksperimental"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index ca45d24..70cf7f8 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Пренареждане на бързите настройки"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Показване на яркостта от бързите настройки"</string>
     <string name="experimental" msgid="6198182315536726162">"Експериментални"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index 7df68ac..fd61c8b 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"দ্রুত সেটিংসে পুনরায় সাজান"</string>
     <string name="show_brightness" msgid="6613930842805942519">"দ্রুত সেটিংসে উজ্জ্বলতা দেখান"</string>
     <string name="experimental" msgid="6198182315536726162">"পরীক্ষামূলক"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index c85fac6..8eac5d9 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Torna a ordenar la Configuració ràpida"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Mostra la brillantor a la Configuració ràpida"</string>
     <string name="experimental" msgid="6198182315536726162">"Experimental"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index d58b11b..e1b0256 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -444,4 +444,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Změnit uspořádání Rychlého nastavení"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Zobrazit jas v Rychlém nastavení"</string>
     <string name="experimental" msgid="6198182315536726162">"Experimentální"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index f300b3e..745bf5a 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -85,13 +85,13 @@
     <string name="accessibility_search_light" msgid="1103867596330271848">"Søg"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
     <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
-    <string name="accessibility_voice_assist_button" msgid="487611083884852965">"Voice Assist"</string>
+    <string name="accessibility_voice_assist_button" msgid="487611083884852965">"Taleassistent"</string>
     <string name="accessibility_unlock_button" msgid="128158454631118828">"Lås op"</string>
     <string name="accessibility_unlock_button_fingerprint" msgid="8214125623493923751">"Knap til oplåsning. Venter på fingeraftryk"</string>
     <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Lås op uden at bruge dit fingeraftryk"</string>
     <string name="unlock_label" msgid="8779712358041029439">"lås op"</string>
     <string name="phone_label" msgid="2320074140205331708">"åbn telefon"</string>
-    <string name="voice_assist_label" msgid="3956854378310019854">"åbn voice assist"</string>
+    <string name="voice_assist_label" msgid="3956854378310019854">"åbn taleassistent"</string>
     <string name="camera_label" msgid="7261107956054836961">"åbn kamera"</string>
     <string name="recents_caption_resize" msgid="3517056471774958200">"Vælg nyt opgavelayout"</string>
     <string name="cancel" msgid="6442560571259935130">"Annuller"</string>
@@ -315,7 +315,7 @@
     <string name="notification_tap_again" msgid="8524949573675922138">"Tryk igen for at åbne"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"Stryg opad for at låse op"</string>
     <string name="phone_hint" msgid="4872890986869209950">"Stryg fra telefonikonet"</string>
-    <string name="voice_hint" msgid="8939888732119726665">"Stryg fra ikonet for voice assist"</string>
+    <string name="voice_hint" msgid="8939888732119726665">"Stryg fra mikrofonikonet"</string>
     <string name="camera_hint" msgid="7939688436797157483">"Stryg fra kameraikonet"</string>
     <string name="interruption_level_none_with_warning" msgid="5114872171614161084">"Helt lydløs. Denne handling slukker også skærmlæsere."</string>
     <string name="interruption_level_none" msgid="6000083681244492992">"Total stilhed"</string>
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Omarranger Hurtige indstillinger"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Vis lysstyrke i Hurtige indstillinger"</string>
     <string name="experimental" msgid="6198182315536726162">"Eksperimentel"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index ac513e40..796cce2 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Schnelleinstellungen neu anordnen"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Helligkeit in den Schnelleinstellungen anzeigen"</string>
     <string name="experimental" msgid="6198182315536726162">"Experimentell"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index c8ff7c6..1dcfb7b 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Αναδιάταξη Γρήγορων ρυθμίσεων"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Εμφάνιση φωτεινότητας στις Γρήγορες ρυθμίσεις"</string>
     <string name="experimental" msgid="6198182315536726162">"Σε πειραματικό στάδιο"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 6e66842..d991f16 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Rearrange Quick Settings"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Show brightness in Quick Settings"</string>
     <string name="experimental" msgid="6198182315536726162">"Experimental"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 6e66842..d991f16 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Rearrange Quick Settings"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Show brightness in Quick Settings"</string>
     <string name="experimental" msgid="6198182315536726162">"Experimental"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 6e66842..d991f16 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Rearrange Quick Settings"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Show brightness in Quick Settings"</string>
     <string name="experimental" msgid="6198182315536726162">"Experimental"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 72258fa..4eef099 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar la Configuración rápida"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Mostrar el brillo en la Configuración rápida"</string>
     <string name="experimental" msgid="6198182315536726162">"Experimental"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 298a0c6..5dfb878 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar Ajustes rápidos"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Mostrar brillo en Ajustes rápidos"</string>
     <string name="experimental" msgid="6198182315536726162">"Experimental"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 26b3081..d3d51209 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Korralda kiirseaded ümber"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Kuva kiirseadetes heledus"</string>
     <string name="experimental" msgid="6198182315536726162">"Eksperimentaalne"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index 83aac20..d48373c 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Berrantolatu ezarpen bizkorrak"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Erakutsi distira Ezarpen bizkorretan"</string>
     <string name="experimental" msgid="6198182315536726162">"Esperimentala"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 3efd33d..f9e2b22 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"ترتیب مجدد در تنظیمات سریع"</string>
     <string name="show_brightness" msgid="6613930842805942519">"نمایش روشنایی در تنظیمات سریع"</string>
     <string name="experimental" msgid="6198182315536726162">"آزمایشی"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 3154d5c..7816337 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Järjestä pika-asetukset uudelleen"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Näytä kirkkaus pika-asetuksissa"</string>
     <string name="experimental" msgid="6198182315536726162">"Kokeellinen"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 669ca9b..66c435c 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Réorganiser les paramètres rapides"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Afficher la luminosité dans les paramètres rapides"</string>
     <string name="experimental" msgid="6198182315536726162">"Fonctions expérimentales"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index d42190f..5b39201 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Réorganiser la fenêtre de configuration rapide"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Afficher la luminosité dans fenêtre de configuration rapide"</string>
     <string name="experimental" msgid="6198182315536726162">"Paramètres expérimentaux"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index 3c53983..e5329ed 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar Configuración rápida"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Mostrar brillo en Configuración rápida"</string>
     <string name="experimental" msgid="6198182315536726162">"Experimental"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-gu-rIN/strings.xml b/packages/SystemUI/res/values-gu-rIN/strings.xml
index 0887e75..38594be 100644
--- a/packages/SystemUI/res/values-gu-rIN/strings.xml
+++ b/packages/SystemUI/res/values-gu-rIN/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"ઝડપી સેટિંગ્સને ફરીથી ગોઠવો"</string>
     <string name="show_brightness" msgid="6613930842805942519">"ઝડપી સેટિંગ્સમાં તેજ બતાવો"</string>
     <string name="experimental" msgid="6198182315536726162">"પ્રાયોગિક"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 11a0d3a..2abf37b 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"त्वरित सेटिंग को पुन: व्यवस्थित करें"</string>
     <string name="show_brightness" msgid="6613930842805942519">"त्वरित सेटिंग में स्क्रीन की रोशनी दिखाएं"</string>
     <string name="experimental" msgid="6198182315536726162">"प्रयोगात्मक"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index eec3a6f..30f22b7 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -441,4 +441,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Promijeni raspored Brzih postavki"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Prikaži svjetlinu u Brzim postavkama"</string>
     <string name="experimental" msgid="6198182315536726162">"Eksperimentalno"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 2dc9ba0..e1a57f9 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Gyorsbeállítások átrendezése"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Fényerő megjelenítése a gyorsbeállításokban"</string>
     <string name="experimental" msgid="6198182315536726162">"Kísérleti"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index a7fdb43..6124ef2 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Վերադասավորել Արագ կարգավորումները"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Ցույց տալ պայծառությունն Արագ կարգավորումներում"</string>
     <string name="experimental" msgid="6198182315536726162">"Փորձնական"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index a6a68d4..2ce7a5b 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Atur Ulang Setelan Cepat"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Tampilkan kecerahan di Setelan Cepat"</string>
     <string name="experimental" msgid="6198182315536726162">"Eksperimental"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index 83468e5..cb95cde 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Endurraða flýtistillingum"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Sýna birtustig í flýtistillingum"</string>
     <string name="experimental" msgid="6198182315536726162">"Tilraunastillingar"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 4449a0a..1e0cec7 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Riorganizza Impostazioni rapide"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Mostra luminosità in Impostazioni rapide"</string>
     <string name="experimental" msgid="6198182315536726162">"Sperimentali"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 31c5126..88c6fbd 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"סידור מחדש של הגדרות מהירות"</string>
     <string name="show_brightness" msgid="6613930842805942519">"הצג בהירות בהגדרות מהירות"</string>
     <string name="experimental" msgid="6198182315536726162">"ניסיוניות"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index d1603ff..6006f64 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"クイック設定を並べ替え"</string>
     <string name="show_brightness" msgid="6613930842805942519">"クイック設定に明るさ調整バーを表示する"</string>
     <string name="experimental" msgid="6198182315536726162">"試験運用版"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index e5e9003..f74a434 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"სწრაფი პარამეტრების გადაწყობა"</string>
     <string name="show_brightness" msgid="6613930842805942519">"სიკაშკაშის ჩვენება სწრაფ პარამეტრებში"</string>
     <string name="experimental" msgid="6198182315536726162">"ექსპერიმენტული"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index 946d86b..4483303 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Жылдам параметрлерді қайта реттеу"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Жылдам параметрлерде жарықтықты көрсету"</string>
     <string name="experimental" msgid="6198182315536726162">"Эксперименттік"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index 264438b..9b17ffc 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"រៀបចំការកំណត់រហ័សឡើងវិញ"</string>
     <string name="show_brightness" msgid="6613930842805942519">"បង្ហាញកម្រិតពន្លឺនៅក្នុងការកំណត់រហ័ស"</string>
     <string name="experimental" msgid="6198182315536726162">"ពិសោធន៍"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index 1696076..7401340 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‌‌ಗಳನ್ನು ಮರುಹೊಂದಿಸಿ"</string>
     <string name="show_brightness" msgid="6613930842805942519">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‌‌ಗಳಲ್ಲಿ ಪ್ರಖರತೆಯನ್ನು ತೋರಿಸಿ"</string>
     <string name="experimental" msgid="6198182315536726162">"ಪ್ರಾಯೋಗಿಕ"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index d053b53..1f7fec7 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"빠른 설정 재정렬"</string>
     <string name="show_brightness" msgid="6613930842805942519">"빠른 설정에서 밝기 표시"</string>
     <string name="experimental" msgid="6198182315536726162">"베타"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index ec4bec7..00ce218 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Ыкчам жөндөөлөрдү кайра коюу"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Ыкчам жөндөөлөрдөн жарык деңгээлин көрсөтүү"</string>
     <string name="experimental" msgid="6198182315536726162">"Сынамык"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index e5d784f..92dbb4d 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"ຈັດ​ວາງ​ການ​ຕັ້ງ​ຄ່າ​ດ່ວນ​ຄືນ​ໃໝ່"</string>
     <string name="show_brightness" msgid="6613930842805942519">"ສະ​ແດງ​ຄວາມ​ແຈ້ງ​ຢູ່​ໃນ​ການ​ຕັ້ງ​ຄ່າ​ດ່ວນ"</string>
     <string name="experimental" msgid="6198182315536726162">"ຍັງຢູ່ໃນການທົດລອງ"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index ca50956..304afe4 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Pertvarkyti sparčiuosius nustatymus"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Rodyti skaistį sparčiuosiuose nustatymuose"</string>
     <string name="experimental" msgid="6198182315536726162">"Eksperimentinė versija"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index a87d918..a1a437d 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -441,4 +441,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Pārkārtot ātros iestatījumus"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Rādīt spilgtumu ātrajos iestatījumos"</string>
     <string name="experimental" msgid="6198182315536726162">"Eksperimentāli"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index 1015018..808943b 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Преуредете ги Брзи поставки"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Прикажете ја осветленоста во Брзи поставки"</string>
     <string name="experimental" msgid="6198182315536726162">"Експериментално"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index 45ec29a..7724f34 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"ദ്രുത ക്രമീകരണം പുനഃസജ്ജീകരിക്കുക"</string>
     <string name="show_brightness" msgid="6613930842805942519">"ദ്രുത ക്രമീകരണത്തിൽ തെളിച്ചം കാണിക്കുക"</string>
     <string name="experimental" msgid="6198182315536726162">"പരീക്ഷണാത്മകം!"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index 1051e49..cecf5af 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -438,4 +438,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Түргэн тохиргоог дахин засварлах"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Түргэн тохиргоонд гэрэлтүүлэг харах"</string>
     <string name="experimental" msgid="6198182315536726162">"Туршилтын"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml
index 66843fc..b253849 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"द्रुत सेटिंग्जची पुनर्रचना करा"</string>
     <string name="show_brightness" msgid="6613930842805942519">"द्रुत सेटिंग्जमध्‍ये चमक दर्शवा"</string>
     <string name="experimental" msgid="6198182315536726162">"प्रायोगिक"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index c50e214..5a94581 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Susun Semula Tetapan Pantas"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Tunjukkan kecerahan dalam Tetapan Pantas"</string>
     <string name="experimental" msgid="6198182315536726162">"Percubaan"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index f4fd3d1..855df4d 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"အမြန် ဆက်တင်များကို ပြန်စီစဉ်ရန်"</string>
     <string name="show_brightness" msgid="6613930842805942519">"အမြန် ဆက်တင်များထဲက တောက်ပမှုကို ပြရန်"</string>
     <string name="experimental" msgid="6198182315536726162">"စမ်းသပ်ရေး"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index cae404d..d662464 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Omorganiser hurtiginnstillingene"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Vis lysstyrke i hurtiginnstillingene"</string>
     <string name="experimental" msgid="6198182315536726162">"På forsøksstadiet"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index 563bc87..8333fb0 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"द्रुत सेटिङहरू पुनः व्यवस्थित गर्नुहोस्"</string>
     <string name="show_brightness" msgid="6613930842805942519">"द्रुत सेटिङहरूमा उज्यालो देखाउनुहोस्"</string>
     <string name="experimental" msgid="6198182315536726162">"प्रयोगात्मक"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index bf9f0f2..3bca7c4 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Snelle instellingen opnieuw indelen"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Helderheid weergeven in Snelle instellingen"</string>
     <string name="experimental" msgid="6198182315536726162">"Experimenteel"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pa-rIN/strings.xml b/packages/SystemUI/res/values-pa-rIN/strings.xml
index 45d56cc..d431999 100644
--- a/packages/SystemUI/res/values-pa-rIN/strings.xml
+++ b/packages/SystemUI/res/values-pa-rIN/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਨੂੰ ਦੁਬਾਰਾ ਕ੍ਰਮ ਦਿਓ"</string>
     <string name="show_brightness" msgid="6613930842805942519">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਚਮਕ ਦਿਖਾਓ"</string>
     <string name="experimental" msgid="6198182315536726162">"ਪ੍ਰਯੋਗਾਤਮਿਕ"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 2807c5a..e1a57be 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Uporządkuj Szybkie ustawienia"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Pokaż jasność w Szybkich ustawieniach"</string>
     <string name="experimental" msgid="6198182315536726162">"Eksperymentalne"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index c8400c9..e94318b 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar \"Configurações rápidas\""</string>
     <string name="show_brightness" msgid="6613930842805942519">"Mostrar brilho nas \"Configurações rápidas\""</string>
     <string name="experimental" msgid="6198182315536726162">"Experimentais"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 43088fe9..a41ee93 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar as Definições rápidas"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Mostrar luminosidade nas Definições rápidas"</string>
     <string name="experimental" msgid="6198182315536726162">"Experimental"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index c8400c9..e94318b 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Reorganizar \"Configurações rápidas\""</string>
     <string name="show_brightness" msgid="6613930842805942519">"Mostrar brilho nas \"Configurações rápidas\""</string>
     <string name="experimental" msgid="6198182315536726162">"Experimentais"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index d64ca7c..64560b9 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -441,4 +441,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Rearanjați Setările rapide"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Afișați luminozitatea în Setările rapide"</string>
     <string name="experimental" msgid="6198182315536726162">"Experimentale"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 480de30..7487393 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -444,4 +444,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Изменить порядок Быстрых настроек"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Добавить яркость в Быстрые настройки"</string>
     <string name="experimental" msgid="6198182315536726162">"Экспериментальная функция"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index e784601..c54f0ff 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"ඉක්මන් සැකසීම් යළි පිළිවෙළට සකසන්න"</string>
     <string name="show_brightness" msgid="6613930842805942519">"ඉක්මන් සැකසීම්වල දීප්තිය පෙන්වන්න"</string>
     <string name="experimental" msgid="6198182315536726162">"පරීක්ෂණාත්මක"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 15612ee..adde8be 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -444,4 +444,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Zmeniť usporiadanie Rýchlych nastavení"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Zobraziť jas v Rýchlych nastaveniach"</string>
     <string name="experimental" msgid="6198182315536726162">"Experimentálne"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 23e4233..2542807 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Preuredi hitre nastavitve"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Prikaz svetlosti v hitrih nastavitvah"</string>
     <string name="experimental" msgid="6198182315536726162">"Poskusno"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sq-rAL/strings.xml b/packages/SystemUI/res/values-sq-rAL/strings.xml
index 771ad53..a710477 100644
--- a/packages/SystemUI/res/values-sq-rAL/strings.xml
+++ b/packages/SystemUI/res/values-sq-rAL/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Risistemo Cilësimet e shpejta"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Shfaq ndriçimin te Cilësimet e shpejta"</string>
     <string name="experimental" msgid="6198182315536726162">"Eksperimentale"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index f3abc81..c171a60 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -441,4 +441,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Преуреди Брза подешавања"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Прикажи осветљеност у Брзим подешавањима"</string>
     <string name="experimental" msgid="6198182315536726162">"Експериментално"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 6427644..fae5ab9 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Ordna snabbinställningarna"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Visa ljusstyrka i snabbinställningarna"</string>
     <string name="experimental" msgid="6198182315536726162">"Experimentella"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index de39e9b..5b46a7e 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Panga Upya Mipangilio ya Haraka"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Onyesha unga\'avu katika Mipangilio ya Haraka"</string>
     <string name="experimental" msgid="6198182315536726162">"Ya majaribio"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index a895df7..477b715 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"விரைவு அமைப்புகளை மறுவரிசைப்படுத்து"</string>
     <string name="show_brightness" msgid="6613930842805942519">"விரைவு அமைப்புகளில் ஒளிர்வுப் பட்டியைக் காட்டு"</string>
     <string name="experimental" msgid="6198182315536726162">"சோதனை முயற்சி"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index c34a33d..f718669a 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"శీఘ్ర సెట్టింగ్‌ల ఏర్పాటు క్రమం మార్చు"</string>
     <string name="show_brightness" msgid="6613930842805942519">"శీఘ్ర సెట్టింగ్‌ల్లో ప్రకాశం చూపు"</string>
     <string name="experimental" msgid="6198182315536726162">"ప్రయోగాత్మకం"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index d777a0a..ca4000a 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"จัดเรียงการตั้งค่าด่วนใหม่"</string>
     <string name="show_brightness" msgid="6613930842805942519">"แสดงความสว่างในการตั้งค่าด่วน"</string>
     <string name="experimental" msgid="6198182315536726162">"ทดสอบ"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 292d3a2..5e8bb7a 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Ayusing Muli ang Mga Mabilisang Setting"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Ipakita ang liwanag sa Mga Mabilisang Setting"</string>
     <string name="experimental" msgid="6198182315536726162">"Pang-eksperimento"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 9c42e93..dbca4c7 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Hızlı Ayarlar\'ı Yeniden Düzenle"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Hızlı Ayarlar\'da parlaklığı göster"</string>
     <string name="experimental" msgid="6198182315536726162">"Deneysel"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 9638e80..8f8793c 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Упорядкувати швидкі налаштування"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Показувати панель яскравості у швидких налаштуваннях"</string>
     <string name="experimental" msgid="6198182315536726162">"Експериментальні налаштування"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index 8c2fb64..086f9e2 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"فوری ترتیبات کو دوبارہ ترتیب دیں"</string>
     <string name="show_brightness" msgid="6613930842805942519">"فوری ترتیبات میں چمکیلا پن دکھائیں"</string>
     <string name="experimental" msgid="6198182315536726162">"تجرباتی"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index 0ad5a69..b1a556a 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Tezkor sozlamalarni qayta tartiblash"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Tezkor sozlamalarda yorqinlikni ko‘rsatish"</string>
     <string name="experimental" msgid="6198182315536726162">"Tajribaviy"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index c1aeeb4..e22909c 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Sắp xếp lại Cài đặt nhanh"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Hiển thị độ sáng trong Cài đặt nhanh"</string>
     <string name="experimental" msgid="6198182315536726162">"Thử nghiệm"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index fe1c311..9d32567 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"重新排列快速设置"</string>
     <string name="show_brightness" msgid="6613930842805942519">"在快速设置中显示亮度栏"</string>
     <string name="experimental" msgid="6198182315536726162">"实验性"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index d39adc0..d80eb72 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"重新排列快速設定"</string>
     <string name="show_brightness" msgid="6613930842805942519">"在快速設定顯示亮度"</string>
     <string name="experimental" msgid="6198182315536726162">"實驗版"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 6606167..e7a4450 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -442,4 +442,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"重新排列快速設定"</string>
     <string name="show_brightness" msgid="6613930842805942519">"在快速設定中顯示亮度"</string>
     <string name="experimental" msgid="6198182315536726162">"實驗性"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 229c16b..06dc0c9 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -440,4 +440,10 @@
     <string name="qs_rearrange" msgid="8060918697551068765">"Hlela kabusha izilungiselelo ezisheshayo"</string>
     <string name="show_brightness" msgid="6613930842805942519">"Bonisa ukukhanya kuzilungiselelo ezisheshayo"</string>
     <string name="experimental" msgid="6198182315536726162">"Okokulinga"</string>
+    <!-- no translation found for enable_bluetooth_title (5027037706500635269) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_message (9106595990708985385) -->
+    <skip />
+    <!-- no translation found for enable_bluetooth_confirmation_ok (6258074250948309715) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5661657..cbc92f2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -125,6 +125,7 @@
     <dimen name="pull_span_min">25dp</dimen>
 
     <dimen name="qs_tile_height">88dp</dimen>
+    <dimen name="qs_new_tile_height">100dp</dimen>
     <dimen name="qs_quick_actions_height">88dp</dimen>
     <dimen name="qs_quick_actions_padding">25dp</dimen>
     <dimen name="qs_page_indicator_size">12dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a525fbb..6fe7e6a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1167,4 +1167,14 @@
     <string name="qs_customize_info" translatable="false">Info</string>
     <string name="qs_customize_remove" translatable="false">Remove</string>
 
+    <!-- Dialog title asking if Bluetooth should be enabled [CHAR LIMIT=NONE] -->
+    <string name="enable_bluetooth_title">Turn on Bluetooth?</string>
+
+    <!-- Dialog message explaining why Bluetooth should be enabled when a packaged keyboard is
+         conncted to the device [CHAR LIMIT=NONE] -->
+    <string name="enable_bluetooth_message">To connect your keyboard with your tablet, you first have to turn on Bluetooth.</string>
+
+    <!-- Bluetooth enablement ok text [CHAR LIMIT=40] -->
+    <string name="enable_bluetooth_confirmation_ok">Turn on</string>
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 5bf251b..9551fb7 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -50,6 +50,7 @@
             com.android.systemui.usb.StorageNotification.class,
             com.android.systemui.power.PowerUI.class,
             com.android.systemui.media.RingtonePlayer.class,
+            com.android.systemui.keyboard.KeyboardUI.class,
     };
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialog.java b/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialog.java
new file mode 100644
index 0000000..64f3e13f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialog.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+
+public class BluetoothDialog extends SystemUIDialog {
+
+    public BluetoothDialog(Context context) {
+        super(context);
+
+        getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+        setShowForAllUsers(true);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
new file mode 100644
index 0000000..96ee397
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard;
+
+import android.app.AlertDialog;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanRecord;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.hardware.input.InputManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings.Secure;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.view.WindowManager;
+
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeChangedListener {
+    private static final String TAG = "KeyboardUI";
+    private static final boolean DEBUG = false;
+
+    // Give BT some time to start after SyUI comes up. This avoids flashing a dialog in the user's
+    // face because BT starts a little bit later in the boot process than SysUI and it takes some
+    // time for us to receive the signal that it's starting.
+    private static final long BLUETOOTH_START_DELAY_MILLIS = 10 * 1000;
+
+    private static final int STATE_NOT_ENABLED = -1;
+    private static final int STATE_UNKNOWN = 0;
+    private static final int STATE_WAITING_FOR_BOOT_COMPLETED = 1;
+    private static final int STATE_WAITING_FOR_TABLET_MODE_EXIT = 2;
+    private static final int STATE_WAITING_FOR_DEVICE_DISCOVERY = 3;
+    private static final int STATE_WAITING_FOR_BLUETOOTH = 4;
+    private static final int STATE_WAITING_FOR_STATE_PAIRED = 5;
+    private static final int STATE_PAIRING = 6;
+    private static final int STATE_PAIRED = 7;
+    private static final int STATE_USER_CANCELLED = 8;
+    private static final int STATE_DEVICE_NOT_FOUND = 9;
+
+    private static final int MSG_INIT = 0;
+    private static final int MSG_ON_BOOT_COMPLETED = 1;
+    private static final int MSG_PROCESS_KEYBOARD_STATE = 2;
+    private static final int MSG_ENABLE_BLUETOOTH = 3;
+    private static final int MSG_ON_BLUETOOTH_STATE_CHANGED = 4;
+    private static final int MSG_ON_DEVICE_BOND_STATE_CHANGED = 5;
+    private static final int MSG_ON_BLUETOOTH_DEVICE_ADDED = 6;
+    private static final int MSG_ON_BLE_SCAN_FAILED = 7;
+    private static final int MSG_SHOW_BLUETOOTH_DIALOG = 8;
+    private static final int MSG_DISMISS_BLUETOOTH_DIALOG = 9;
+
+    private volatile KeyboardHandler mHandler;
+    private volatile KeyboardUIHandler mUIHandler;
+
+    protected volatile Context mContext;
+
+    private boolean mEnabled;
+    private String mKeyboardName;
+    private CachedBluetoothDeviceManager mCachedDeviceManager;
+    private LocalBluetoothAdapter mLocalBluetoothAdapter;
+    private LocalBluetoothProfileManager mProfileManager;
+    private boolean mBootCompleted;
+    private long mBootCompletedTime;
+
+    private int mInTabletMode = InputManager.SWITCH_STATE_UNKNOWN;
+    private ScanCallback mScanCallback;
+    private BluetoothDialog mDialog;
+
+    private int mState;
+
+    @Override
+    public void start() {
+        mContext = super.mContext;
+        HandlerThread thread = new HandlerThread("Keyboard", Process.THREAD_PRIORITY_BACKGROUND);
+        thread.start();
+        mHandler = new KeyboardHandler(thread.getLooper());
+        mHandler.sendEmptyMessage(MSG_INIT);
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("KeyboardUI:");
+        pw.println("  mEnabled=" + mEnabled);
+        pw.println("  mBootCompleted=" + mEnabled);
+        pw.println("  mBootCompletedTime=" + mBootCompletedTime);
+        pw.println("  mKeyboardName=" + mKeyboardName);
+        pw.println("  mInTabletMode=" + mInTabletMode);
+        pw.println("  mState=" + stateToString(mState));
+    }
+
+    @Override
+    protected void onBootCompleted() {
+        mHandler.sendEmptyMessage(MSG_ON_BOOT_COMPLETED);
+    }
+
+    @Override
+    public void onTabletModeChanged(long whenNanos, boolean inTabletMode) {
+        if (DEBUG) {
+            Slog.d(TAG, "onTabletModeChanged(" + whenNanos + ", " + inTabletMode + ")");
+        }
+
+        if (inTabletMode && mInTabletMode != InputManager.SWITCH_STATE_ON
+                || !inTabletMode && mInTabletMode != InputManager.SWITCH_STATE_OFF) {
+            mInTabletMode = inTabletMode ?
+                    InputManager.SWITCH_STATE_ON : InputManager.SWITCH_STATE_OFF;
+            processKeyboardState();
+        }
+    }
+
+    // Shoud only be called on the handler thread
+    private void init() {
+        Context context = mContext;
+        mKeyboardName =
+                context.getString(com.android.internal.R.string.config_packagedKeyboardName);
+        if (TextUtils.isEmpty(mKeyboardName)) {
+            if (DEBUG) {
+                Slog.d(TAG, "No packaged keyboard name given.");
+            }
+            return;
+        }
+
+        LocalBluetoothManager bluetoothManager = LocalBluetoothManager.getInstance(context, null);
+        if (bluetoothManager == null)  {
+            if (DEBUG) {
+                Slog.e(TAG, "Failed to retrieve LocalBluetoothManager instance");
+            }
+            return;
+        }
+        mEnabled = true;
+        mCachedDeviceManager = bluetoothManager.getCachedDeviceManager();
+        mLocalBluetoothAdapter = bluetoothManager.getBluetoothAdapter();
+        mProfileManager = bluetoothManager.getProfileManager();
+        bluetoothManager.getEventManager().registerCallback(new BluetoothCallbackHandler());
+
+        InputManager im = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
+        im.registerOnTabletModeChangedListener(this, mHandler);
+        mInTabletMode = im.isInTabletMode();
+
+        processKeyboardState();
+        mUIHandler = new KeyboardUIHandler();
+    }
+
+    // Should only be called on the handler thread
+    private void processKeyboardState() {
+        mHandler.removeMessages(MSG_PROCESS_KEYBOARD_STATE);
+
+        if (!mEnabled) {
+            mState = STATE_NOT_ENABLED;
+            return;
+        }
+
+        if (!mBootCompleted) {
+            mState = STATE_WAITING_FOR_BOOT_COMPLETED;
+            return;
+        }
+
+        if (mInTabletMode != InputManager.SWITCH_STATE_OFF) {
+            if (mState == STATE_WAITING_FOR_DEVICE_DISCOVERY) {
+                stopScanning();
+            }
+            mState = STATE_WAITING_FOR_TABLET_MODE_EXIT;
+            return;
+        }
+
+        final int btState = mLocalBluetoothAdapter.getState();
+        if (btState == BluetoothAdapter.STATE_TURNING_ON || btState == BluetoothAdapter.STATE_ON
+                && mState == STATE_WAITING_FOR_BLUETOOTH) {
+            // If we're waiting for bluetooth but it has come on in the meantime, or is coming
+            // on, just dismiss the dialog. This frequently happens during device startup.
+            mUIHandler.sendEmptyMessage(MSG_DISMISS_BLUETOOTH_DIALOG);
+        }
+
+        if (btState == BluetoothAdapter.STATE_TURNING_ON) {
+            mState = STATE_WAITING_FOR_BLUETOOTH;
+            // Wait for bluetooth to fully come on.
+            return;
+        }
+
+        if (btState != BluetoothAdapter.STATE_ON) {
+            mState = STATE_WAITING_FOR_BLUETOOTH;
+            showBluetoothDialog();
+            return;
+        }
+
+        CachedBluetoothDevice device = getPairedKeyboard();
+        if (mState == STATE_WAITING_FOR_TABLET_MODE_EXIT || mState == STATE_WAITING_FOR_BLUETOOTH) {
+            if (device != null) {
+                // If we're just coming out of tablet mode or BT just turned on,
+                // then we want to go ahead and automatically connect to the
+                // keyboard. We want to avoid this in other cases because we might
+                // be spuriously called after the user has manually disconnected
+                // the keyboard, meaning we shouldn't try to automtically connect
+                // it again.
+                mState = STATE_PAIRED;
+                device.connect(false);
+                return;
+            }
+            mCachedDeviceManager.clearNonBondedDevices();
+        }
+
+        device = getDiscoveredKeyboard();
+        if (device != null) {
+            mState = STATE_PAIRING;
+            device.startPairing();
+        } else {
+            mState = STATE_WAITING_FOR_DEVICE_DISCOVERY;
+            startScanning();
+        }
+    }
+
+    // Should only be called on the handler thread
+    public void onBootCompletedInternal() {
+        mBootCompleted = true;
+        mBootCompletedTime = SystemClock.uptimeMillis();
+        if (mState == STATE_WAITING_FOR_BOOT_COMPLETED) {
+            processKeyboardState();
+        }
+    }
+
+    // Should only be called on the handler thread
+    private void showBluetoothDialog() {
+        if (isUserSetupComplete()) {
+            long now = SystemClock.uptimeMillis();
+            long earliestDialogTime = mBootCompletedTime + BLUETOOTH_START_DELAY_MILLIS;
+            if (earliestDialogTime < now) {
+                mUIHandler.sendEmptyMessage(MSG_SHOW_BLUETOOTH_DIALOG);
+            } else {
+                mHandler.sendEmptyMessageAtTime(MSG_PROCESS_KEYBOARD_STATE, earliestDialogTime);
+            }
+        } else {
+            // If we're in setup wizard and the keyboard is docked, just automatically enable BT.
+            mLocalBluetoothAdapter.enable();
+        }
+    }
+
+    private boolean isUserSetupComplete() {
+        ContentResolver resolver = mContext.getContentResolver();
+        return Secure.getIntForUser(
+                resolver, Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+    }
+
+    private CachedBluetoothDevice getPairedKeyboard() {
+        Set<BluetoothDevice> devices = mLocalBluetoothAdapter.getBondedDevices();
+        for (BluetoothDevice d : devices) {
+            if (mKeyboardName.equals(d.getName())) {
+                return getCachedBluetoothDevice(d);
+            }
+        }
+        return null;
+    }
+
+    private CachedBluetoothDevice getDiscoveredKeyboard() {
+        Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
+        for (CachedBluetoothDevice d : devices) {
+            if (d.getName().equals(mKeyboardName)) {
+                return d;
+            }
+        }
+        return null;
+    }
+
+
+    private CachedBluetoothDevice getCachedBluetoothDevice(BluetoothDevice d) {
+        CachedBluetoothDevice cachedDevice = mCachedDeviceManager.findDevice(d);
+        if (cachedDevice == null) {
+            cachedDevice = mCachedDeviceManager.addDevice(
+                    mLocalBluetoothAdapter, mProfileManager, d);
+        }
+        return cachedDevice;
+    }
+
+    private void startScanning() {
+        BluetoothLeScanner scanner = mLocalBluetoothAdapter.getBluetoothLeScanner();
+        ScanFilter filter = (new ScanFilter.Builder()).setDeviceName(mKeyboardName).build();
+        ScanSettings settings = (new ScanSettings.Builder())
+            .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
+            .setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
+            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
+            .setReportDelay(0)
+            .build();
+        mScanCallback = new KeyboardScanCallback();
+        scanner.startScan(Arrays.asList(filter), settings, mScanCallback);
+    }
+
+    private void stopScanning() {
+        if (mScanCallback != null) {
+            mLocalBluetoothAdapter.getBluetoothLeScanner().stopScan(mScanCallback);
+            mScanCallback = null;
+        }
+    }
+
+    // Should only be called on the handler thread
+    private void onDeviceAddedInternal(CachedBluetoothDevice d) {
+        if (mState == STATE_WAITING_FOR_DEVICE_DISCOVERY && d.getName().equals(mKeyboardName)) {
+            stopScanning();
+            d.startPairing();
+            mState = STATE_PAIRING;
+        }
+    }
+
+    // Should only be called on the handler thread
+    private void onBluetoothStateChangedInternal(int bluetoothState) {
+        if (bluetoothState == BluetoothAdapter.STATE_ON && mState == STATE_WAITING_FOR_BLUETOOTH) {
+            processKeyboardState();
+        }
+    }
+
+    // Should only be called on the handler thread
+    private void onDeviceBondStateChangedInternal(CachedBluetoothDevice d, int bondState) {
+        if (d.getName().equals(mKeyboardName) && bondState == BluetoothDevice.BOND_BONDED) {
+            // We don't need to manually connect to the device here because it will automatically
+            // try to connect after it has been paired.
+            mState = STATE_PAIRED;
+        }
+    }
+
+    // Should only be called on the handler thread
+    private void onBleScanFailedInternal() {
+        mScanCallback = null;
+        if (mState == STATE_WAITING_FOR_DEVICE_DISCOVERY) {
+            mState = STATE_DEVICE_NOT_FOUND;
+        }
+    }
+
+    private final class KeyboardUIHandler extends Handler {
+        public KeyboardUIHandler() {
+            super(Looper.getMainLooper(), null, true /*async*/);
+        }
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case MSG_SHOW_BLUETOOTH_DIALOG: {
+                    DialogInterface.OnClickListener listener = new BluetoothDialogClickListener();
+                    mDialog = new BluetoothDialog(mContext);
+                    mDialog.setTitle(R.string.enable_bluetooth_title);
+                    mDialog.setMessage(R.string.enable_bluetooth_message);
+                    mDialog.setPositiveButton(R.string.enable_bluetooth_confirmation_ok, listener);
+                    mDialog.setNegativeButton(android.R.string.cancel, listener);
+                    mDialog.show();
+                    break;
+                }
+                case MSG_DISMISS_BLUETOOTH_DIALOG: {
+                    if (mDialog != null) {
+                        mDialog.dismiss();
+                        mDialog = null;
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
+    private final class KeyboardHandler extends Handler {
+        public KeyboardHandler(Looper looper) {
+            super(looper, null, true /*async*/);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case MSG_INIT: {
+                    init();
+                    break;
+                }
+                case MSG_ON_BOOT_COMPLETED: {
+                    onBootCompletedInternal();
+                    break;
+                }
+                case MSG_PROCESS_KEYBOARD_STATE: {
+                    processKeyboardState();
+                    break;
+                }
+                case MSG_ENABLE_BLUETOOTH: {
+                    boolean enable = msg.arg1 == 1;
+                    if (enable) {
+                        mLocalBluetoothAdapter.enable();
+                    } else {
+                        mState = STATE_USER_CANCELLED;
+                    }
+                }
+                case MSG_ON_BLUETOOTH_STATE_CHANGED: {
+                    int bluetoothState = msg.arg1;
+                    onBluetoothStateChangedInternal(bluetoothState);
+                    break;
+                }
+                case MSG_ON_DEVICE_BOND_STATE_CHANGED: {
+                    CachedBluetoothDevice d = (CachedBluetoothDevice)msg.obj;
+                    int bondState = msg.arg1;
+                    onDeviceBondStateChangedInternal(d, bondState);
+                    break;
+                }
+                case MSG_ON_BLUETOOTH_DEVICE_ADDED: {
+                    BluetoothDevice d = (BluetoothDevice)msg.obj;
+                    CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(d);
+                    onDeviceAddedInternal(cachedDevice);
+                    break;
+
+                }
+                case MSG_ON_BLE_SCAN_FAILED: {
+                    onBleScanFailedInternal();
+                    break;
+                }
+            }
+        }
+    }
+
+    private final class BluetoothDialogClickListener implements DialogInterface.OnClickListener {
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            int enable = DialogInterface.BUTTON_POSITIVE == which ? 1 : 0;
+            mHandler.obtainMessage(MSG_ENABLE_BLUETOOTH, enable, 0).sendToTarget();
+            mDialog = null;
+        }
+    }
+
+    private final class KeyboardScanCallback extends ScanCallback {
+
+        private boolean isDeviceDiscoverable(ScanResult result) {
+            final ScanRecord scanRecord = result.getScanRecord();
+            final int flags = scanRecord.getAdvertiseFlags();
+            final int BT_DISCOVERABLE_MASK = 0x03;
+
+            return (flags & BT_DISCOVERABLE_MASK) != 0;
+        }
+
+        @Override
+        public void onBatchScanResults(List<ScanResult> results) {
+            if (DEBUG) {
+                Slog.d(TAG, "onBatchScanResults(" + results.size() + ")");
+            }
+
+            BluetoothDevice bestDevice = null;
+            int bestRssi = Integer.MIN_VALUE;
+
+            for (ScanResult result : results) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onBatchScanResults: considering " + result);
+                }
+
+                if (isDeviceDiscoverable(result) && result.getRssi() > bestRssi) {
+                    bestDevice = result.getDevice();
+                    bestRssi = result.getRssi();
+                }
+            }
+
+            if (bestDevice != null) {
+                mHandler.obtainMessage(MSG_ON_BLUETOOTH_DEVICE_ADDED, bestDevice).sendToTarget();
+            }
+        }
+
+        @Override
+        public void onScanFailed(int errorCode) {
+            if (DEBUG) {
+                Slog.d(TAG, "onScanFailed(" + errorCode + ")");
+            }
+            mHandler.obtainMessage(MSG_ON_BLE_SCAN_FAILED).sendToTarget();
+        }
+
+        @Override
+        public void onScanResult(int callbackType, ScanResult result) {
+            if (DEBUG) {
+                Slog.d(TAG, "onScanResult(" + callbackType + ", " + result + ")");
+            }
+
+            if (isDeviceDiscoverable(result)) {
+                mHandler.obtainMessage(MSG_ON_BLUETOOTH_DEVICE_ADDED,
+                        result.getDevice()).sendToTarget();
+            } else if (DEBUG) {
+                Slog.d(TAG, "onScanResult: device " + result.getDevice() +
+                       " is not discoverable, ignoring");
+            }
+        }
+    }
+
+    private final class BluetoothCallbackHandler implements BluetoothCallback {
+        @Override
+        public void onBluetoothStateChanged(int bluetoothState) {
+            mHandler.obtainMessage(MSG_ON_BLUETOOTH_STATE_CHANGED,
+                    bluetoothState, 0).sendToTarget();
+        }
+
+        @Override
+        public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
+            mHandler.obtainMessage(MSG_ON_DEVICE_BOND_STATE_CHANGED,
+                    bondState, 0, cachedDevice).sendToTarget();
+        }
+
+        @Override
+        public void onDeviceAdded(CachedBluetoothDevice cachedDevice) { }
+        @Override
+        public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) { }
+        @Override
+        public void onScanningStateChanged(boolean started) { }
+        @Override
+        public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
+    }
+
+    private static String stateToString(int state) {
+        switch (state) {
+            case STATE_NOT_ENABLED:
+                return "STATE_NOT_ENABLED";
+            case STATE_WAITING_FOR_BOOT_COMPLETED:
+                return "STATE_WAITING_FOR_BOOT_COMPLETED";
+            case STATE_WAITING_FOR_TABLET_MODE_EXIT:
+                return "STATE_WAITING_FOR_TABLET_MODE_EXIT";
+            case STATE_WAITING_FOR_DEVICE_DISCOVERY:
+                return "STATE_WAITING_FOR_DEVICE_DISCOVERY";
+            case STATE_WAITING_FOR_BLUETOOTH:
+                return "STATE_WAITING_FOR_BLUETOOTH";
+            case STATE_WAITING_FOR_STATE_PAIRED:
+                return "STATE_WAITING_FOR_STATE_PAIRED";
+            case STATE_PAIRING:
+                return "STATE_PAIRING";
+            case STATE_PAIRED:
+                return "STATE_PAIRED";
+            case STATE_USER_CANCELLED:
+                return "STATE_USER_CANCELLED";
+            case STATE_DEVICE_NOT_FOUND:
+                return "STATE_DEVICE_NOT_FOUND";
+            case STATE_UNKNOWN:
+            default:
+                return "STATE_UNKNOWN";
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 32c906e..0e4a4e5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -75,8 +75,6 @@
     @Override
     public void setTileVisibility(TileRecord tile, int visibility) {
         tile.tileView.setVisibility(visibility);
-//        // TODO: Do something smarter here.
-//        distributeTiles();
     }
 
     @Override
@@ -104,8 +102,7 @@
         for (int i = 0; i < NT; i++) {
             TileRecord tile = mTiles.get(i);
             if (tile.tile.getTileType() == QSTileView.QS_TYPE_QUICK) {
-                tile.tileView.setType(QSTileView.QS_TYPE_QUICK);
-                mFirstPage.mQuickQuickTiles.addView(tile.tileView);
+                // Don't show any quick tiles for now.
                 continue;
             }
             if (mPages.get(index).isFull()) {
@@ -161,6 +158,7 @@
         protected void onFinishInflate() {
             super.onFinishInflate();
             mQuickQuickTiles = (LinearLayout) findViewById(R.id.quick_tile_layout);
+            mQuickQuickTiles.setVisibility(View.GONE);
             mTilePage = (TilePage) findViewById(R.id.tile_page);
             // Less rows on first page, because it needs room for the quick tiles.
             mTilePage.mMaxRows = 3;
@@ -176,7 +174,7 @@
     }
 
     public static class TilePage extends TileLayout {
-        private int mMaxRows = 4;
+        private int mMaxRows = 3;
 
         public TilePage(Context context, AttributeSet attrs) {
             super(context, attrs);
@@ -188,6 +186,11 @@
             mMaxRows = maxRows;
         }
 
+        @Override
+        protected int getCellHeight() {
+            return mContext.getResources().getDimensionPixelSize(R.dimen.qs_new_tile_height);
+        }
+
         private void clear() {
             if (DEBUG) Log.d(TAG, "Clearing page");
             removeAllViews();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 53d0402..a2d9ef0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -60,7 +60,7 @@
             if (tile.getTileType() == QSTileView.QS_TYPE_QUICK) {
                 quickTiles.add(tile);
             }
-            if (quickTiles.size() == 4) {
+            if (quickTiles.size() == 2) {
                 break;
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index b8342e2..12a099d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -67,7 +67,7 @@
     public void updateResources() {
         final Resources res = mContext.getResources();
         final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
-        mCellHeight = res.getDimensionPixelSize(R.dimen.qs_tile_height);
+        mCellHeight = getCellHeight();
         mCellWidth = (int) (mCellHeight * TILE_ASPECT);
         mLargeCellHeight = mAllowDual ? res.getDimensionPixelSize(R.dimen.qs_dual_tile_height)
                 : mCellHeight;
@@ -79,6 +79,10 @@
         }
     }
 
+    protected int getCellHeight() {
+        return mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         final int width = MeasureSpec.getSize(widthMeasureSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
index 012633c..1669278 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
@@ -61,6 +61,7 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mQuickTiles = (QuickTileLayout) findViewById(R.id.quick_tile_layout);
+        mQuickTiles.setVisibility(View.GONE);
         TilePage page = (PagedTileLayout.TilePage) findViewById(R.id.tile_page);
         page.setMaxRows(3 /* First page only gets 3 */);
         mPages.add(page);
@@ -107,12 +108,12 @@
         for (int i = 0; i < NT; i++) {
             TileRecord tile = mTiles.get(i);
             if (tile.tile.getTileType() == QSTileView.QS_TYPE_QUICK) {
-                tile.tileView.setType(QSTileView.QS_TYPE_QUICK);
-                mQuickTiles.addView(tile.tileView);
+                // Ignore quick tiles for now.
                 continue;
             }
             mPages.get(index).addTile(tile);
-            if (mPages.get(index).isFull()) {
+            // Keep everything in one layout for now.
+            if (false && mPages.get(index).isFull()) {
                 if (++index == mPages.size()) {
                     LayoutInflater inflater = LayoutInflater.from(mContext);
                     inflater.inflate(R.layout.horizontal_divider, this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 601961b..fe8d78b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -84,7 +84,7 @@
         TypedValue value = new TypedValue();
         mContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true);
         mToolbar.setNavigationIcon(
-                getResources().getDrawable(value.resourceId, mContext.getTheme()));
+                getResources().getDrawable(R.drawable.ic_close_white, mContext.getTheme()));
         mToolbar.setNavigationOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
@@ -193,7 +193,8 @@
     @Override
     public void onClick(View v) {
         if (mFab == v) {
-            // TODO: Show list of tiles.
+            SystemUIDialog dialog = new SystemUIDialog(mContext);
+            dialog.show();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
new file mode 100644
index 0000000..79eca30d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+/**
+ * Due to the fact that RecentsActivity is per-user, we need to establish an
+ * interface (this) for the system user to callback to the secondary users in
+ * response to UI events coming in from the system user's SystemUI.
+ */
+oneway interface IRecentsNonSystemUserCallbacks {
+    void preloadRecents();
+    void cancelPreloadingRecents();
+    void showRecents(boolean triggeredFromAltTab);
+    void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
+    void toggleRecents();
+    void onConfigurationChanged();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
new file mode 100644
index 0000000..6b49195
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+/**
+ * Due to the fact that RecentsActivity is per-user, we need to establish an
+ * interface (this) for the non-system user to register itself for callbacks and to
+ * callback to the system user to update internal state.
+ */
+oneway interface IRecentsSystemUserCallbacks {
+    void registerNonSystemUserCallbacks(IBinder nonSystemUserCallbacks, int userId);
+
+    void updateRecentsVisibility(boolean visible);
+    void startScreenPinning();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index aee3623..ae79fe2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -16,908 +16,385 @@
 
 package com.android.systemui.recents;
 
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.app.ITaskStackListener;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
+import android.content.ServiceConnection;
 import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.os.AsyncTask;
 import android.os.Handler;
-import android.os.SystemClock;
+import android.os.IBinder;
+import android.os.RemoteException;
 import android.os.UserHandle;
-import android.util.MutableBoolean;
+import android.util.Log;
 import android.view.Display;
-import android.view.LayoutInflater;
 import android.view.View;
-import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Prefs;
-import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.SystemUI;
-import com.android.systemui.SystemUIApplication;
-import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
+import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.model.TaskGrouping;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.TaskStackView;
-import com.android.systemui.recents.views.TaskStackViewLayoutAlgorithm;
-import com.android.systemui.recents.views.TaskViewHeader;
-import com.android.systemui.recents.views.TaskViewTransform;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
 import java.util.ArrayList;
 
-/**
- * Annotation for a method that is only called from the system user's SystemUI process and will be
- * proxied to the current user.
- */
-@interface ProxyFromSystemToCurrentUser {}
-/**
- * Annotation for a method that may be called from any user's SystemUI process and will be proxied
- * to the system user.
- */
-@interface ProxyFromAnyToSystemUser {}
 
-/** A proxy implementation for the recents component */
+/**
+ * An implementation of the SystemUI recents component, which supports both system and secondary
+ * users.
+ */
 public class Recents extends SystemUI
-        implements ActivityOptions.OnAnimationStartedListener, RecentsComponent {
+        implements RecentsComponent {
+
+    private final static String TAG = "Recents";
+    private final static boolean DEBUG = false;
 
     public final static int EVENT_BUS_PRIORITY = 1;
+    public final static int BIND_TO_SYSTEM_USER_RETRY_DELAY = 5000;
 
-    final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "triggeredFromAltTab";
-    final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "triggeredFromHomeKey";
-    final public static String EXTRA_RECENTS_VISIBILITY = "recentsVisibility";
+    private SystemServicesProxy mSystemServicesProxy;
+    private Handler mHandler;
+    private RecentsImpl mImpl;
 
-    // Owner proxy events
-    final public static String ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER =
-            "action_notify_recents_visibility_change";
-    final public static String ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER =
-            "action_screen_pinning_request";
+    // Only For system user, this is the callbacks instance we return to each secondary user
+    private RecentsSystemUser mSystemUserCallbacks;
 
-    final public static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
-    final public static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
-    final public static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
+    // Only for secondary users, this is the callbacks instance provided by the system user to make
+    // calls back
+    private IRecentsSystemUserCallbacks mCallbacksToSystemUser;
 
-    final static int sMinToggleDelay = 350;
+    // The set of runnables to run after binding to the system user's service.
+    private final ArrayList<Runnable> mOnConnectRunnables = new ArrayList<>();
 
-    public final static String sToggleRecentsAction = "com.android.systemui.recents.SHOW_RECENTS";
-    public final static String sRecentsPackage = "com.android.systemui";
-    public final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity";
-
-    /**
-     * An implementation of ITaskStackListener, that allows us to listen for changes to the system
-     * task stacks and update recents accordingly.
-     */
-    class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable {
-        Handler mHandler;
-
-        public TaskStackListenerImpl(Handler handler) {
-            mHandler = handler;
-        }
-
+    // Only for secondary users, this is the death handler for the binder from the system user
+    private final IBinder.DeathRecipient mCallbacksToSystemUserDeathRcpt = new IBinder.DeathRecipient() {
         @Override
-        public void onTaskStackChanged() {
-            // Debounce any task stack changes
-            mHandler.removeCallbacks(this);
-            mHandler.post(this);
-        }
+        public void binderDied() {
+            mCallbacksToSystemUser = null;
 
-        /** Preloads the next task */
-        public void run() {
-            // TODO: Temporarily skip this if multi stack is enabled
-            /*
-            RecentsConfiguration config = RecentsConfiguration.getInstance();
-            if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
-                RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
-                SystemServicesProxy ssp = loader.getSystemServicesProxy();
-                ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getTopMostTask();
-
-                // Load the next task only if we aren't svelte
-                RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
-                loader.preloadTasks(plan, true);
-                RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
-                // This callback is made when a new activity is launched and the old one is paused
-                // so ignore the current activity and try and preload the thumbnail for the
-                // previous one.
-                if (runningTaskInfo != null) {
-                    launchOpts.runningTaskId = runningTaskInfo.id;
+            // Retry after a fixed duration
+            mHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    registerWithSystemUser();
                 }
-                launchOpts.numVisibleTasks = 2;
-                launchOpts.numVisibleTaskThumbnails = 2;
-                launchOpts.onlyLoadForCache = true;
-                launchOpts.onlyLoadPausedActivities = true;
-                loader.loadTasks(mContext, plan, launchOpts);
-            }
-            */
+            }, BIND_TO_SYSTEM_USER_RETRY_DELAY);
         }
-    }
+    };
 
-    /**
-     * A proxy for Recents events which happens strictly for the owner.
-     */
-    class RecentsOwnerEventProxyReceiver extends BroadcastReceiver {
+    // Only for secondary users, this is the service connection we use to connect to the system user
+    private final ServiceConnection mServiceConnectionToSystemUser = new ServiceConnection() {
         @Override
-        public void onReceive(Context context, Intent intent) {
-            switch (intent.getAction()) {
-                case ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER:
-                    visibilityChanged(context,
-                            intent.getBooleanExtra(EXTRA_RECENTS_VISIBILITY, false));
-                    break;
-                case ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER:
-                    onStartScreenPinning(context);
-                    break;
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (service != null) {
+                mCallbacksToSystemUser = IRecentsSystemUserCallbacks.Stub.asInterface(
+                        service);
+
+                // Listen for system user's death, so that we can reconnect later
+                try {
+                    service.linkToDeath(mCallbacksToSystemUserDeathRcpt, 0);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Lost connection to (System) SystemUI", e);
+                }
+
+                // Run each of the queued runnables
+                runAndFlushOnConnectRunnables();
             }
+
+            // Unbind ourselves now that we've registered our callbacks.  The
+            // binder to the system user are still valid at this point.
+            mContext.unbindService(this);
         }
-    }
 
-    static RecentsTaskLoadPlan sInstanceLoadPlan;
-    static Recents sInstance;
-
-    SystemServicesProxy mSystemServicesProxy;
-    Handler mHandler;
-    TaskStackListenerImpl mTaskStackListener;
-    RecentsOwnerEventProxyReceiver mProxyBroadcastReceiver;
-    RecentsAppWidgetHost mAppWidgetHost;
-    boolean mBootCompleted;
-    boolean mStartAnimationTriggered;
-    boolean mCanReuseTaskStackViews = true;
-
-    // Task launching
-    RecentsConfiguration mConfig;
-    Rect mSearchBarBounds = new Rect();
-    Rect mTaskStackBounds = new Rect();
-    Rect mLastTaskViewBounds = new Rect();
-    TaskViewTransform mTmpTransform = new TaskViewTransform();
-    int mStatusBarHeight;
-    int mNavBarHeight;
-    int mNavBarWidth;
-    int mTaskBarHeight;
-
-    // Header (for transition)
-    TaskViewHeader mHeaderBar;
-    final Object mHeaderBarLock = new Object();
-    TaskStackView mDummyStackView;
-
-    // Variables to keep track of if we need to start recents after binding
-    boolean mTriggeredFromAltTab;
-    long mLastToggleTime;
-
-    Bitmap mThumbnailTransitionBitmapCache;
-    Task mThumbnailTransitionBitmapCacheKey;
-
-    public Recents() {
-    }
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            // Do nothing
+        }
+    };
 
     /**
-     * Gets the singleton instance and starts it if needed. On the primary user on the device, this
-     * component gets started as a normal {@link SystemUI} component. On a secondary user, this
-     * lifecycle doesn't exist, so we need to start it manually here if needed.
+     * Returns the callbacks interface that non-system users can call.
      */
-    public static Recents getInstanceAndStartIfNeeded(Context ctx) {
-        if (sInstance == null) {
-            sInstance = new Recents();
-            sInstance.mContext = ctx;
-            sInstance.start();
-            sInstance.onBootCompleted();
-        }
-        return sInstance;
+    public IBinder getSystemUserCallbacks() {
+        return mSystemUserCallbacks;
     }
 
-    /** Creates a new broadcast intent */
-    static Intent createLocalBroadcastIntent(Context context, String action) {
-        Intent intent = new Intent(action);
-        intent.setPackage(context.getPackageName());
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
-                Intent.FLAG_RECEIVER_FOREGROUND);
-        return intent;
-    }
-
-    /** Initializes the Recents. */
-    @ProxyFromSystemToCurrentUser
     @Override
     public void start() {
-        if (sInstance == null) {
-            sInstance = this;
-        }
-        Resources res = mContext.getResources();
-        RecentsTaskLoader.initialize(mContext);
-        LayoutInflater inflater = LayoutInflater.from(mContext);
         mSystemServicesProxy = new SystemServicesProxy(mContext);
         mHandler = new Handler();
-        mAppWidgetHost = new RecentsAppWidgetHost(mContext, Constants.Values.App.AppWidgetHostId);
+        mImpl = new RecentsImpl(mContext);
 
-        // Register the task stack listener
-        mTaskStackListener = new TaskStackListenerImpl(mHandler);
-        mSystemServicesProxy.registerTaskStackListener(mTaskStackListener);
+        // Register with the event bus
+        EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
 
-        // Only the owner has the callback to update the SysUI visibility flags, so all non-owner
-        // instances of RecentsComponent needs to notify the owner when the visibility
-        // changes.
-        if (mSystemServicesProxy.isForegroundUserSystem()) {
-            mProxyBroadcastReceiver = new RecentsOwnerEventProxyReceiver();
-            IntentFilter filter = new IntentFilter();
-            filter.addAction(Recents.ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER);
-            filter.addAction(Recents.ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER);
-            mContext.registerReceiverAsUser(mProxyBroadcastReceiver, UserHandle.CURRENT, filter,
-                    null, mHandler);
+        // Due to the fact that RecentsActivity is per-user, we need to establish and interface for
+        // the system user's Recents component to pass events (like show/hide/toggleRecents) to the
+        // secondary user, and vice versa (like visibility change, screen pinning).
+        final int processUser = mSystemServicesProxy.getProcessUser();
+        if (mSystemServicesProxy.isSystemUser(processUser)) {
+            // For the system user, initialize an instance of the interface that we can pass to the
+            // secondary user
+            mSystemUserCallbacks = new RecentsSystemUser(mContext, mImpl);
+        } else {
+            // For the secondary user, bind to the primary user's service to get a persistent
+            // interface to register its implementation and to later update its state
+            registerWithSystemUser();
         }
-
-        // Initialize the static configuration resources
-        mConfig = RecentsConfiguration.initialize(mContext, mSystemServicesProxy);
-        mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
-        mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
-        mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
-        mTaskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
-        mDummyStackView = new TaskStackView(mContext, new TaskStack());
-        mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
-                null, false);
-        reloadHeaderBarLayout(true /* tryAndBindSearchWidget */);
-
-        // When we start, preload the data associated with the previous recent tasks.
-        // We can use a new plan since the caches will be the same.
-        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
-        RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
-        loader.preloadTasks(plan, true /* isTopTaskHome */);
-        RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
-        launchOpts.numVisibleTasks = loader.getApplicationIconCacheSize();
-        launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
-        launchOpts.onlyLoadForCache = true;
-        loader.loadTasks(mContext, plan, launchOpts);
         putComponent(Recents.class, this);
     }
 
     @Override
     public void onBootCompleted() {
-        mBootCompleted = true;
-        reloadHeaderBarLayout(true /* tryAndBindSearchWidget */);
+        mImpl.onBootCompleted();
     }
 
-    /** Shows the Recents. */
-    @ProxyFromSystemToCurrentUser
+    /**
+     * Shows the Recents.
+     */
     @Override
     public void showRecents(boolean triggeredFromAltTab, View statusBarView) {
-        if (mSystemServicesProxy.isForegroundUserSystem()) {
-            showRecentsInternal(triggeredFromAltTab);
+        int currentUser = mSystemServicesProxy.getCurrentUser();
+        if (mSystemServicesProxy.isSystemUser(currentUser)) {
+            mImpl.showRecents(triggeredFromAltTab);
         } else {
-            Intent intent = createLocalBroadcastIntent(mContext,
-                    RecentsUserEventProxyReceiver.ACTION_PROXY_SHOW_RECENTS_TO_USER);
-            intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
-            mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+            if (mSystemUserCallbacks != null) {
+                IRecentsNonSystemUserCallbacks callbacks =
+                        mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                if (callbacks != null) {
+                    try {
+                        callbacks.showRecents(triggeredFromAltTab);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                } else {
+                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+                }
+            }
         }
     }
 
-    void showRecentsInternal(boolean triggeredFromAltTab) {
-        mTriggeredFromAltTab = triggeredFromAltTab;
-
-        try {
-            showRecentsActivity();
-        } catch (ActivityNotFoundException e) {
-            Console.logRawError("Failed to launch RecentAppsIntent", e);
-        }
-    }
-
-    /** Hides the Recents. */
-    @ProxyFromSystemToCurrentUser
+    /**
+     * Hides the Recents.
+     */
     @Override
     public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
-        if (mSystemServicesProxy.isForegroundUserSystem()) {
-            hideRecentsInternal(triggeredFromAltTab, triggeredFromHomeKey);
+        int currentUser = mSystemServicesProxy.getCurrentUser();
+        if (mSystemServicesProxy.isSystemUser(currentUser)) {
+            mImpl.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
         } else {
-            Intent intent = createLocalBroadcastIntent(mContext,
-                    RecentsUserEventProxyReceiver.ACTION_PROXY_HIDE_RECENTS_TO_USER);
-            intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
-            intent.putExtra(EXTRA_TRIGGERED_FROM_HOME_KEY, triggeredFromHomeKey);
-            mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+            if (mSystemUserCallbacks != null) {
+                IRecentsNonSystemUserCallbacks callbacks =
+                        mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                if (callbacks != null) {
+                    try {
+                        callbacks.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                } else {
+                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+                }
+            }
         }
     }
 
-    void hideRecentsInternal(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
-        if (mBootCompleted) {
-            // Defer to the activity to handle hiding recents, if it handles it, then it must still
-            // be visible
-            Intent intent = createLocalBroadcastIntent(mContext, ACTION_HIDE_RECENTS_ACTIVITY);
-            intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
-            intent.putExtra(EXTRA_TRIGGERED_FROM_HOME_KEY, triggeredFromHomeKey);
-            mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
-        }
-    }
-
-    /** Toggles the Recents activity. */
-    @ProxyFromSystemToCurrentUser
+    /**
+     * Toggles the Recents activity.
+     */
     @Override
     public void toggleRecents(Display display, int layoutDirection, View statusBarView) {
-        if (mSystemServicesProxy.isForegroundUserSystem()) {
-            toggleRecentsInternal();
+        int currentUser = mSystemServicesProxy.getCurrentUser();
+        if (mSystemServicesProxy.isSystemUser(currentUser)) {
+            mImpl.toggleRecents();
         } else {
-            Intent intent = createLocalBroadcastIntent(mContext,
-                    RecentsUserEventProxyReceiver.ACTION_PROXY_TOGGLE_RECENTS_TO_USER);
-            mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+            if (mSystemUserCallbacks != null) {
+                IRecentsNonSystemUserCallbacks callbacks =
+                        mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                if (callbacks != null) {
+                    try {
+                        callbacks.toggleRecents();
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                } else {
+                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+                }
+            }
         }
     }
 
-    void toggleRecentsInternal() {
-        mTriggeredFromAltTab = false;
-
-        try {
-            toggleRecentsActivity();
-        } catch (ActivityNotFoundException e) {
-            Console.logRawError("Failed to launch RecentAppsIntent", e);
-        }
-    }
-
-    /** Preloads info for the Recents activity. */
-    @ProxyFromSystemToCurrentUser
+    /**
+     * Preloads info for the Recents activity.
+     */
     @Override
     public void preloadRecents() {
-        if (mSystemServicesProxy.isForegroundUserSystem()) {
-            preloadRecentsInternal();
+        int currentUser = mSystemServicesProxy.getCurrentUser();
+        if (mSystemServicesProxy.isSystemUser(currentUser)) {
+            mImpl.preloadRecents();
         } else {
-            Intent intent = createLocalBroadcastIntent(mContext,
-                    RecentsUserEventProxyReceiver.ACTION_PROXY_PRELOAD_RECENTS_TO_USER);
-            mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
-        }
-    }
-
-    void preloadRecentsInternal() {
-        // Preload only the raw task list into a new load plan (which will be consumed by the
-        // RecentsActivity) only if there is a task to animate to.
-        ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
-        MutableBoolean topTaskHome = new MutableBoolean(true);
-        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
-        sInstanceLoadPlan = loader.createLoadPlan(mContext);
-        if (topTask != null && !mSystemServicesProxy.isRecentsTopMost(topTask, topTaskHome)) {
-            sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);
-            loader.preloadTasks(sInstanceLoadPlan, topTaskHome.value);
-            TaskStack stack = sInstanceLoadPlan.getTaskStack();
-            if (stack.getTaskCount() > 0) {
-                preCacheThumbnailTransitionBitmapAsync(topTask, stack, mDummyStackView,
-                        topTaskHome.value);
+            if (mSystemUserCallbacks != null) {
+                IRecentsNonSystemUserCallbacks callbacks =
+                        mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                if (callbacks != null) {
+                    try {
+                        callbacks.preloadRecents();
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                } else {
+                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+                }
             }
         }
     }
 
     @Override
     public void cancelPreloadingRecents() {
-        // Do nothing
-    }
-
-    void showRelativeAffiliatedTask(boolean showNextTask) {
-        // Return early if there is no focused stack
-        int focusedStackId = mSystemServicesProxy.getFocusedStack();
-        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
-        RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
-        loader.preloadTasks(plan, true /* isTopTaskHome */);
-        TaskStack focusedStack = plan.getTaskStack();
-
-        // Return early if there are no tasks in the focused stack
-        if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
-
-        ActivityManager.RunningTaskInfo runningTask = mSystemServicesProxy.getTopMostTask();
-        // Return early if there is no running task (can't determine affiliated tasks in this case)
-        if (runningTask == null) return;
-        // Return early if the running task is in the home stack (optimization)
-        if (mSystemServicesProxy.isInHomeStack(runningTask.id)) return;
-
-        // Find the task in the recents list
-        ArrayList<Task> tasks = focusedStack.getTasks();
-        Task toTask = null;
-        ActivityOptions launchOpts = null;
-        int taskCount = tasks.size();
-        int numAffiliatedTasks = 0;
-        for (int i = 0; i < taskCount; i++) {
-            Task task = tasks.get(i);
-            if (task.key.id == runningTask.id) {
-                TaskGrouping group = task.group;
-                Task.TaskKey toTaskKey;
-                if (showNextTask) {
-                    toTaskKey = group.getNextTaskInGroup(task);
-                    launchOpts = ActivityOptions.makeCustomAnimation(mContext,
-                            R.anim.recents_launch_next_affiliated_task_target,
-                            R.anim.recents_launch_next_affiliated_task_source);
-                } else {
-                    toTaskKey = group.getPrevTaskInGroup(task);
-                    launchOpts = ActivityOptions.makeCustomAnimation(mContext,
-                            R.anim.recents_launch_prev_affiliated_task_target,
-                            R.anim.recents_launch_prev_affiliated_task_source);
-                }
-                if (toTaskKey != null) {
-                    toTask = focusedStack.findTaskWithId(toTaskKey.id);
-                }
-                numAffiliatedTasks = group.getTaskCount();
-                break;
-            }
-        }
-
-        // Return early if there is no next task
-        if (toTask == null) {
-            if (numAffiliatedTasks > 1) {
-                if (showNextTask) {
-                    mSystemServicesProxy.startInPlaceAnimationOnFrontMostApplication(
-                            ActivityOptions.makeCustomInPlaceAnimation(mContext,
-                                    R.anim.recents_launch_next_affiliated_task_bounce));
-                } else {
-                    mSystemServicesProxy.startInPlaceAnimationOnFrontMostApplication(
-                            ActivityOptions.makeCustomInPlaceAnimation(mContext,
-                                    R.anim.recents_launch_prev_affiliated_task_bounce));
-                }
-            }
-            return;
-        }
-
-        // Keep track of actually launched affiliated tasks
-        MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
-
-        // Launch the task
-        if (toTask.isActive) {
-            // Bring an active task to the foreground
-            mSystemServicesProxy.moveTaskToFront(toTask.key.id, launchOpts);
+        int currentUser = mSystemServicesProxy.getCurrentUser();
+        if (mSystemServicesProxy.isSystemUser(currentUser)) {
+            mImpl.cancelPreloadingRecents();
         } else {
-            mSystemServicesProxy.startActivityFromRecents(mContext, toTask.key.id,
-                    toTask.activityLabel, launchOpts);
+            if (mSystemUserCallbacks != null) {
+                IRecentsNonSystemUserCallbacks callbacks =
+                        mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                if (callbacks != null) {
+                    try {
+                        callbacks.cancelPreloadingRecents();
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                } else {
+                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+                }
+            }
         }
     }
 
     @Override
     public void showNextAffiliatedTask() {
-        // Keep track of when the affiliated task is triggered
-        MetricsLogger.count(mContext, "overview_affiliated_task_next", 1);
-        showRelativeAffiliatedTask(true);
+        mImpl.showNextAffiliatedTask();
     }
 
     @Override
     public void showPrevAffiliatedTask() {
-        // Keep track of when the affiliated task is triggered
-        MetricsLogger.count(mContext, "overview_affiliated_task_prev", 1);
-        showRelativeAffiliatedTask(false);
+        mImpl.showPrevAffiliatedTask();
     }
 
-    /** Updates on configuration change. */
-    @ProxyFromSystemToCurrentUser
+    /**
+     * Updates on configuration change.
+     */
     public void onConfigurationChanged(Configuration newConfig) {
-        if (mSystemServicesProxy.isForegroundUserSystem()) {
-            configurationChanged();
+        int currentUser = mSystemServicesProxy.getCurrentUser();
+        if (mSystemServicesProxy.isSystemUser(currentUser)) {
+            mImpl.onConfigurationChanged();
         } else {
-            Intent intent = createLocalBroadcastIntent(mContext,
-                    RecentsUserEventProxyReceiver.ACTION_PROXY_CONFIG_CHANGE_TO_USER);
-            mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
-        }
-    }
-    void configurationChanged() {
-        // Don't reuse task stack views if the configuration changes
-        mCanReuseTaskStackViews = false;
-        mConfig.updateOnConfigurationChange();
-    }
-
-    /**
-     * Prepares the header bar layout for the next transition, if the task view bounds has changed
-     * since the last call, it will attempt to re-measure and layout the header bar to the new size.
-     *
-     * @param tryAndBindSearchWidget if set, will attempt to fetch and bind the search widget if one
-     *                               is not already bound (can be expensive)
-     */
-    void reloadHeaderBarLayout(boolean tryAndBindSearchWidget) {
-        Rect windowRect = mSystemServicesProxy.getWindowRect();
-
-        // Update the configuration for the current state
-        mConfig.update(mContext, mSystemServicesProxy, mSystemServicesProxy.getWindowRect());
-
-        if (tryAndBindSearchWidget) {
-            // Try and pre-emptively bind the search widget on startup to ensure that we
-            // have the right thumbnail bounds to animate to.
-            // Note: We have to reload the widget id before we get the task stack bounds below
-            if (mSystemServicesProxy.getOrBindSearchAppWidget(mContext, mAppWidgetHost) != null) {
-                mConfig.getSearchBarBounds(windowRect,
-                        mStatusBarHeight, mSearchBarBounds);
-            }
-        }
-        Rect systemInsets = new Rect(0, mStatusBarHeight,
-                (mConfig.hasTransposedNavBar ? mNavBarWidth : 0),
-                (mConfig.hasTransposedNavBar ? 0 : mNavBarHeight));
-        mConfig.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right,
-                mSearchBarBounds, mTaskStackBounds);
-
-        // Rebind the header bar and draw it for the transition
-        TaskStackViewLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
-        Rect taskStackBounds = new Rect(mTaskStackBounds);
-        algo.setSystemInsets(systemInsets);
-        algo.computeRects(windowRect.width(), windowRect.height(), taskStackBounds);
-        Rect taskViewBounds = algo.getUntransformedTaskViewBounds();
-        if (!taskViewBounds.equals(mLastTaskViewBounds)) {
-            mLastTaskViewBounds.set(taskViewBounds);
-
-            int taskViewWidth = taskViewBounds.width();
-            synchronized (mHeaderBarLock) {
-                mHeaderBar.measure(
-                    View.MeasureSpec.makeMeasureSpec(taskViewWidth, View.MeasureSpec.EXACTLY),
-                    View.MeasureSpec.makeMeasureSpec(mTaskBarHeight, View.MeasureSpec.EXACTLY));
-                mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
-            }
-        }
-    }
-
-    /** Toggles the recents activity */
-    void toggleRecentsActivity() {
-        // If the user has toggled it too quickly, then just eat up the event here (it's better than
-        // showing a janky screenshot).
-        // NOTE: Ideally, the screenshot mechanism would take the window transform into account
-        if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) {
-            return;
-        }
-
-        // If Recents is the front most activity, then we should just communicate with it directly
-        // to launch the first task or dismiss itself
-        ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
-        MutableBoolean isTopTaskHome = new MutableBoolean(true);
-        if (topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
-            // Notify recents to toggle itself
-            Intent intent = createLocalBroadcastIntent(mContext, ACTION_TOGGLE_RECENTS_ACTIVITY);
-            mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
-            mLastToggleTime = SystemClock.elapsedRealtime();
-            return;
-        } else {
-            // Otherwise, start the recents activity
-            showRecentsActivity(topTask, isTopTaskHome.value);
-        }
-    }
-
-    /** Shows the recents activity if it is not already running */
-    void showRecentsActivity() {
-        // Check if the top task is in the home stack, and start the recents activity
-        ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
-        MutableBoolean isTopTaskHome = new MutableBoolean(true);
-        if (topTask == null || !mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
-            showRecentsActivity(topTask, isTopTaskHome.value);
-        }
-    }
-
-    /**
-     * Creates the activity options for a unknown state->recents transition.
-     */
-    ActivityOptions getUnknownTransitionActivityOptions() {
-        mStartAnimationTriggered = false;
-        return ActivityOptions.makeCustomAnimation(mContext,
-                R.anim.recents_from_unknown_enter,
-                R.anim.recents_from_unknown_exit,
-                mHandler, this);
-    }
-
-    /**
-     * Creates the activity options for a home->recents transition.
-     */
-    ActivityOptions getHomeTransitionActivityOptions(boolean fromSearchHome) {
-        mStartAnimationTriggered = false;
-        if (fromSearchHome) {
-            return ActivityOptions.makeCustomAnimation(mContext,
-                    R.anim.recents_from_search_launcher_enter,
-                    R.anim.recents_from_search_launcher_exit,
-                    mHandler, this);
-        }
-        return ActivityOptions.makeCustomAnimation(mContext,
-                R.anim.recents_from_launcher_enter,
-                R.anim.recents_from_launcher_exit,
-                mHandler, this);
-    }
-
-    /**
-     * Creates the activity options for an app->recents transition.
-     */
-    ActivityOptions getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo topTask,
-            TaskStack stack, TaskStackView stackView) {
-
-        // Update the destination rect
-        Task toTask = new Task();
-        TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
-                topTask.id, toTask);
-        Rect toTaskRect = toTransform.rect;
-        Bitmap thumbnail;
-        if (mThumbnailTransitionBitmapCacheKey != null
-                && mThumbnailTransitionBitmapCacheKey.key != null
-                && mThumbnailTransitionBitmapCacheKey.key.equals(toTask.key)) {
-            thumbnail = mThumbnailTransitionBitmapCache;
-            mThumbnailTransitionBitmapCacheKey = null;
-            mThumbnailTransitionBitmapCache = null;
-        } else {
-            preloadIcon(topTask);
-            thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform);
-        }
-        if (thumbnail != null) {
-            mStartAnimationTriggered = false;
-            return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
-                    thumbnail, toTaskRect.left, toTaskRect.top, toTaskRect.width(),
-                    toTaskRect.height(), mHandler, this);
-        }
-
-        // If both the screenshot and thumbnail fails, then just fall back to the default transition
-        return getUnknownTransitionActivityOptions();
-    }
-
-    /**
-     * Preloads the icon of a task.
-     */
-    void preloadIcon(ActivityManager.RunningTaskInfo task) {
-
-        // Ensure that we load the running task's icon
-        RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
-        launchOpts.runningTaskId = task.id;
-        launchOpts.loadThumbnails = false;
-        launchOpts.onlyLoadForCache = true;
-        RecentsTaskLoader.getInstance().loadTasks(mContext, sInstanceLoadPlan, launchOpts);
-    }
-
-    /**
-     * Caches the header thumbnail used for a window animation asynchronously into
-     * {@link #mThumbnailTransitionBitmapCache}.
-     */
-    void preCacheThumbnailTransitionBitmapAsync(ActivityManager.RunningTaskInfo topTask,
-            TaskStack stack, TaskStackView stackView, boolean isTopTaskHome) {
-        preloadIcon(topTask);
-
-        // Update the header bar if necessary
-        reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
-
-        // Update the destination rect
-        mDummyStackView.updateMinMaxScrollForStack(stack);
-        final Task toTask = new Task();
-        final TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
-                topTask.id, toTask);
-        new AsyncTask<Void, Void, Bitmap>() {
-            @Override
-            protected Bitmap doInBackground(Void... params) {
-                return drawThumbnailTransitionBitmap(toTask, toTransform);
-            }
-
-            @Override
-            protected void onPostExecute(Bitmap bitmap) {
-                mThumbnailTransitionBitmapCache = bitmap;
-                mThumbnailTransitionBitmapCacheKey = toTask;
-            }
-        }.execute();
-    }
-
-    /**
-     * Draws the header of a task used for the window animation into a bitmap.
-     */
-    Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform) {
-        if (toTransform != null && toTask.key != null) {
-            Bitmap thumbnail;
-            synchronized (mHeaderBarLock) {
-                int toHeaderWidth = (int) (mHeaderBar.getMeasuredWidth() * toTransform.scale);
-                int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
-                thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
-                        Bitmap.Config.ARGB_8888);
-                if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
-                    thumbnail.eraseColor(0xFFff0000);
-                } else {
-                    Canvas c = new Canvas(thumbnail);
-                    c.scale(toTransform.scale, toTransform.scale);
-                    mHeaderBar.rebindToTask(toTask);
-                    mHeaderBar.draw(c);
-                    c.setBitmap(null);
-                }
-            }
-            return thumbnail.createAshmemBitmap();
-        }
-        return null;
-    }
-
-    /** Returns the transition rect for the given task id. */
-    TaskViewTransform getThumbnailTransitionTransform(TaskStack stack, TaskStackView stackView,
-            int runningTaskId, Task runningTaskOut) {
-        // Find the running task in the TaskStack
-        Task task = null;
-        ArrayList<Task> tasks = stack.getTasks();
-        if (runningTaskId != -1) {
-            // Otherwise, try and find the task with the
-            int taskCount = tasks.size();
-            for (int i = taskCount - 1; i >= 0; i--) {
-                Task t = tasks.get(i);
-                if (t.key.id == runningTaskId) {
-                    task = t;
-                    runningTaskOut.copyFrom(t);
-                    break;
-                }
-            }
-        }
-        if (task == null) {
-            // If no task is specified or we can not find the task just use the front most one
-            task = tasks.get(tasks.size() - 1);
-            runningTaskOut.copyFrom(task);
-        }
-
-        // Get the transform for the running task
-        stackView.getScroller().setStackScrollToInitialState();
-        mTmpTransform = stackView.getStackAlgorithm().getStackTransform(task,
-                stackView.getScroller().getStackScroll(), mTmpTransform, null);
-        return mTmpTransform;
-    }
-
-    /** Shows the recents activity */
-    void showRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
-        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
-
-        // Update the header bar if necessary
-        reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
-
-        if (sInstanceLoadPlan == null) {
-            // Create a new load plan if onPreloadRecents() was never triggered
-            sInstanceLoadPlan = loader.createLoadPlan(mContext);
-        }
-
-        if (!sInstanceLoadPlan.hasTasks()) {
-            loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
-        }
-        TaskStack stack = sInstanceLoadPlan.getTaskStack();
-
-        // Prepare the dummy stack for the transition
-        mDummyStackView.updateMinMaxScrollForStack(stack);
-        TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
-                mDummyStackView.computeStackVisibilityReport();
-        boolean hasRecentTasks = stack.getTaskCount() > 0;
-        boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
-
-        if (useThumbnailTransition) {
-            // Try starting with a thumbnail transition
-            ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
-                    mDummyStackView);
-            if (opts != null) {
-                startRecentsActivity(topTask, opts, false /* fromHome */,
-                        false /* fromSearchHome */, true /* fromThumbnail */, stackVr);
-            } else {
-                // Fall through below to the non-thumbnail transition
-                useThumbnailTransition = false;
-            }
-        }
-
-        if (!useThumbnailTransition) {
-            // If there is no thumbnail transition, but is launching from home into recents, then
-            // use a quick home transition and do the animation from home
-            if (hasRecentTasks) {
-                String homeActivityPackage = mSystemServicesProxy.getHomeActivityPackageName();
-                String searchWidgetPackage =
-                        Prefs.getString(mContext, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE, null);
-
-                // Determine whether we are coming from a search owned home activity
-                boolean fromSearchHome = (homeActivityPackage != null) &&
-                        homeActivityPackage.equals(searchWidgetPackage);
-                ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
-                startRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
-                        false /* fromThumbnail */, stackVr);
-            } else {
-                // Otherwise we do the normal fade from an unknown source
-                ActivityOptions opts = getUnknownTransitionActivityOptions();
-                startRecentsActivity(topTask, opts, true /* fromHome */,
-                        false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
-            }
-        }
-        mLastToggleTime = SystemClock.elapsedRealtime();
-    }
-
-    /** Starts the recents activity */
-    void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
-              ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
-              TaskStackViewLayoutAlgorithm.VisibilityReport vr) {
-        // Update the configuration based on the launch options
-        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
-        launchState.launchedFromHome = fromSearchHome || fromHome;
-        launchState.launchedFromSearchHome = fromSearchHome;
-        launchState.launchedFromAppWithThumbnail = fromThumbnail;
-        launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
-        launchState.launchedWithAltTab = mTriggeredFromAltTab;
-        launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
-        launchState.launchedNumVisibleTasks = vr.numVisibleTasks;
-        launchState.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
-        launchState.launchedHasConfigurationChanged = false;
-
-        Intent intent = new Intent(sToggleRecentsAction);
-        intent.setClassName(sRecentsPackage, sRecentsActivity);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
-                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
-        if (opts != null) {
-            mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
-        } else {
-            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
-        }
-        mCanReuseTaskStackViews = true;
-    }
-
-    /** Notifies the callbacks that the visibility of Recents has changed. */
-    @ProxyFromAnyToSystemUser
-    public static void notifyVisibilityChanged(Context context, SystemServicesProxy ssp,
-            boolean visible) {
-        if (ssp.isForegroundUserSystem()) {
-            visibilityChanged(context, visible);
-        } else {
-            Intent intent = createLocalBroadcastIntent(context,
-                    ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER);
-            intent.putExtra(EXTRA_RECENTS_VISIBILITY, visible);
-            context.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
-        }
-    }
-    static void visibilityChanged(Context context, boolean visible) {
-        // For the primary user, the context for the SystemUI component is the SystemUIApplication
-        SystemUIApplication app = (SystemUIApplication)
-                getInstanceAndStartIfNeeded(context.getApplicationContext()).mContext;
-        PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
-        if (statusBar != null) {
-            statusBar.updateRecentsVisibility(visible);
-        }
-    }
-
-    /** Notifies the status bar to trigger screen pinning. */
-    @ProxyFromAnyToSystemUser
-    public static void startScreenPinning(Context context, SystemServicesProxy ssp) {
-        if (ssp.isForegroundUserSystem()) {
-            onStartScreenPinning(context);
-        } else {
-            Intent intent = createLocalBroadcastIntent(context,
-                    ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER);
-            context.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
-        }
-    }
-    static void onStartScreenPinning(Context context) {
-        // For the primary user, the context for the SystemUI component is the SystemUIApplication
-        SystemUIApplication app = (SystemUIApplication)
-                getInstanceAndStartIfNeeded(context.getApplicationContext()).mContext;
-        PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
-        if (statusBar != null) {
-            statusBar.showScreenPinningRequest(false);
-        }
-    }
-
-    /**
-     * Returns the preloaded load plan and invalidates it.
-     */
-    public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
-        RecentsTaskLoadPlan plan = sInstanceLoadPlan;
-        sInstanceLoadPlan = null;
-        return plan;
-    }
-
-    /**** OnAnimationStartedListener Implementation ****/
-
-    @Override
-    public void onAnimationStarted() {
-        // Notify recents to start the enter animation
-        if (!mStartAnimationTriggered) {
-            // There can be a race condition between the start animation callback and
-            // the start of the new activity (where we register the receiver that listens
-            // to this broadcast, so we add our own receiver and if that gets called, then
-            // we know the activity has not yet started and we can retry sending the broadcast.
-            BroadcastReceiver fallbackReceiver = new BroadcastReceiver() {
-                @Override
-                public void onReceive(Context context, Intent intent) {
-                    if (getResultCode() == Activity.RESULT_OK) {
-                        mStartAnimationTriggered = true;
-                        return;
+            if (mSystemUserCallbacks != null) {
+                IRecentsNonSystemUserCallbacks callbacks =
+                        mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                if (callbacks != null) {
+                    try {
+                        callbacks.onConfigurationChanged();
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
                     }
-
-                    // Schedule for the broadcast to be sent again after some time
-                    mHandler.postDelayed(new Runnable() {
-                        @Override
-                        public void run() {
-                            onAnimationStarted();
-                        }
-                    }, 25);
+                } else {
+                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
                 }
-            };
-
-            // Send the broadcast to notify Recents that the animation has started
-            Intent intent = createLocalBroadcastIntent(mContext, ACTION_START_ENTER_ANIMATION);
-            mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
-                    fallbackReceiver, null, Activity.RESULT_CANCELED, null, null);
+            }
         }
     }
+
+    /**
+     * Handle Recents activity visibility changed.
+     */
+    public final void onBusEvent(final RecentsVisibilityChangedEvent event) {
+        int processUser = event.systemServicesProxy.getProcessUser();
+        if (event.systemServicesProxy.isSystemUser(processUser)) {
+            mImpl.onVisibilityChanged(event.applicationContext, event.visible);
+        } else {
+            postToSystemUser(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        mCallbacksToSystemUser.updateRecentsVisibility(event.visible);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                }
+            });
+        }
+    }
+
+    /**
+     * Handle screen pinning request.
+     */
+    public final void onBusEvent(final ScreenPinningRequestEvent event) {
+        int processUser = event.systemServicesProxy.getProcessUser();
+        if (event.systemServicesProxy.isSystemUser(processUser)) {
+            mImpl.onStartScreenPinning(event.applicationContext);
+        } else {
+            postToSystemUser(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        mCallbacksToSystemUser.startScreenPinning();
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                }
+            });
+        }
+    }
+
+    /**
+     * Attempts to register with the system user.
+     */
+    private void registerWithSystemUser() {
+        final int processUser = mSystemServicesProxy.getProcessUser();
+        postToSystemUser(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    mCallbacksToSystemUser.registerNonSystemUserCallbacks(mImpl, processUser);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to register", e);
+                }
+            }
+        });
+    }
+
+    /**
+     * Runs the runnable in the system user's Recents context, connecting to the service if
+     * necessary.
+     */
+    private void postToSystemUser(final Runnable onConnectRunnable) {
+        mOnConnectRunnables.add(onConnectRunnable);
+        if (mCallbacksToSystemUser == null) {
+            Intent systemUserServiceIntent = new Intent();
+            systemUserServiceIntent.setClass(mContext, RecentsSystemUserService.class);
+            boolean bound = mContext.bindServiceAsUser(systemUserServiceIntent,
+                    mServiceConnectionToSystemUser, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
+            if (!bound) {
+                // Retry after a fixed duration
+                mHandler.postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        registerWithSystemUser();
+                    }
+                }, BIND_TO_SYSTEM_USER_RETRY_DELAY);
+            }
+        } else {
+            runAndFlushOnConnectRunnables();
+        }
+    }
+
+    /**
+     * Runs all the queued runnables after a service connection is made.
+     */
+    private void runAndFlushOnConnectRunnables() {
+        for (Runnable r : mOnConnectRunnables) {
+            r.run();
+        }
+        mOnConnectRunnables.clear();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index e647c1f..fa1c887 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -39,6 +39,8 @@
 import com.android.systemui.R;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
+import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
 import com.android.systemui.recents.events.ui.DismissTaskEvent;
 import com.android.systemui.recents.events.ui.ResizeTaskEvent;
 import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
@@ -127,20 +129,20 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            if (action.equals(Recents.ACTION_HIDE_RECENTS_ACTIVITY)) {
-                if (intent.getBooleanExtra(Recents.EXTRA_TRIGGERED_FROM_ALT_TAB, false)) {
+            if (action.equals(RecentsImpl.ACTION_HIDE_RECENTS_ACTIVITY)) {
+                if (intent.getBooleanExtra(RecentsImpl.EXTRA_TRIGGERED_FROM_ALT_TAB, false)) {
                     // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
                     dismissRecentsToFocusedTaskOrHome(false);
-                } else if (intent.getBooleanExtra(Recents.EXTRA_TRIGGERED_FROM_HOME_KEY, false)) {
+                } else if (intent.getBooleanExtra(RecentsImpl.EXTRA_TRIGGERED_FROM_HOME_KEY, false)) {
                     // Otherwise, dismiss Recents to Home
                     dismissRecentsToHome(true);
                 } else {
                     // Do nothing
                 }
-            } else if (action.equals(Recents.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
+            } else if (action.equals(RecentsImpl.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
                 // If we are toggling Recents, then first unfilter any filtered stacks first
                 dismissRecentsToFocusedTaskOrHome(true);
-            } else if (action.equals(Recents.ACTION_START_ENTER_ANIMATION)) {
+            } else if (action.equals(RecentsImpl.ACTION_START_ENTER_ANIMATION)) {
                 // Trigger the enter animation
                 onEnterAnimationTriggered();
                 // Notify the fallback receiver that we have successfully got the broadcast
@@ -174,7 +176,7 @@
         // If AlternateRecentsComponent has preloaded a load plan, then use that to prevent
         // reconstructing the task stack
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
-        RecentsTaskLoadPlan plan = Recents.consumeInstanceLoadPlan();
+        RecentsTaskLoadPlan plan = RecentsImpl.consumeInstanceLoadPlan();
         if (plan == null) {
             plan = loader.createLoadPlan(this);
         }
@@ -371,13 +373,15 @@
         MetricsLogger.visible(this, MetricsLogger.OVERVIEW_ACTIVITY);
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
         SystemServicesProxy ssp = loader.getSystemServicesProxy();
-        Recents.notifyVisibilityChanged(this, ssp, true);
+
+        // Notify that recents is now visible
+        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, true));
 
         // Register the broadcast receiver to handle messages from our service
         IntentFilter filter = new IntentFilter();
-        filter.addAction(Recents.ACTION_HIDE_RECENTS_ACTIVITY);
-        filter.addAction(Recents.ACTION_TOGGLE_RECENTS_ACTIVITY);
-        filter.addAction(Recents.ACTION_START_ENTER_ANIMATION);
+        filter.addAction(RecentsImpl.ACTION_HIDE_RECENTS_ACTIVITY);
+        filter.addAction(RecentsImpl.ACTION_TOGGLE_RECENTS_ACTIVITY);
+        filter.addAction(RecentsImpl.ACTION_START_ENTER_ANIMATION);
         registerReceiver(mServiceBroadcastReceiver, filter);
 
         // Register any broadcast receivers for the task loader
@@ -415,7 +419,9 @@
         MetricsLogger.hidden(this, MetricsLogger.OVERVIEW_ACTIVITY);
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
         SystemServicesProxy ssp = loader.getSystemServicesProxy();
-        Recents.notifyVisibilityChanged(this, ssp, false);
+
+        // Notify that recents is now hidden
+        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, false));
 
         // Notify the views that we are no longer visible
         mRecentsView.onRecentsHidden();
@@ -564,7 +570,7 @@
     public void onScreenPinningRequest() {
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
         SystemServicesProxy ssp = loader.getSystemServicesProxy();
-        Recents.startScreenPinning(this, ssp);
+        EventBus.getDefault().send(new ScreenPinningRequestEvent(this, ssp));
 
         MetricsLogger.count(this, "overview_screen_pinned", 1);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
new file mode 100644
index 0000000..2c76087
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -0,0 +1,769 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.ITaskStackListener;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.MutableBoolean;
+import android.view.LayoutInflater;
+import android.view.View;
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.Prefs;
+import com.android.systemui.R;
+import com.android.systemui.SystemUIApplication;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
+import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsTaskLoadPlan;
+import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskGrouping;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.TaskStackView;
+import com.android.systemui.recents.views.TaskStackViewLayoutAlgorithm;
+import com.android.systemui.recents.views.TaskViewHeader;
+import com.android.systemui.recents.views.TaskViewTransform;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+
+import java.util.ArrayList;
+
+/**
+ * An implementation of the Recents component for the current user.  For secondary users, this can
+ * be called remotely from the system user.
+ */
+public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
+        implements ActivityOptions.OnAnimationStartedListener {
+
+    private final static String TAG = "RecentsImpl";
+
+    final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "triggeredFromAltTab";
+    final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "triggeredFromHomeKey";
+
+    final public static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
+    final public static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
+    final public static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
+
+    final static int sMinToggleDelay = 350;
+
+    public final static String RECENTS_PACKAGE = "com.android.systemui";
+    public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";
+
+
+    /**
+     * An implementation of ITaskStackListener, that allows us to listen for changes to the system
+     * task stacks and update recents accordingly.
+     */
+    class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable {
+        Handler mHandler;
+
+        public TaskStackListenerImpl(Handler handler) {
+            mHandler = handler;
+        }
+
+        @Override
+        public void onTaskStackChanged() {
+            // Debounce any task stack changes
+            mHandler.removeCallbacks(this);
+            mHandler.post(this);
+        }
+
+        /** Preloads the next task */
+        public void run() {
+            // TODO: Temporarily skip this if multi stack is enabled
+            /*
+            RecentsConfiguration config = RecentsConfiguration.getInstance();
+            if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
+                RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+                SystemServicesProxy ssp = loader.getSystemServicesProxy();
+                ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getTopMostTask();
+
+                // Load the next task only if we aren't svelte
+                RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
+                loader.preloadTasks(plan, true);
+                RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+                // This callback is made when a new activity is launched and the old one is paused
+                // so ignore the current activity and try and preload the thumbnail for the
+                // previous one.
+                if (runningTaskInfo != null) {
+                    launchOpts.runningTaskId = runningTaskInfo.id;
+                }
+                launchOpts.numVisibleTasks = 2;
+                launchOpts.numVisibleTaskThumbnails = 2;
+                launchOpts.onlyLoadForCache = true;
+                launchOpts.onlyLoadPausedActivities = true;
+                loader.loadTasks(mContext, plan, launchOpts);
+            }
+            */
+        }
+    }
+
+    static RecentsTaskLoadPlan sInstanceLoadPlan;
+
+    Context mContext;
+    SystemServicesProxy mSystemServicesProxy;
+    Handler mHandler;
+    TaskStackListenerImpl mTaskStackListener;
+    RecentsAppWidgetHost mAppWidgetHost;
+    boolean mBootCompleted;
+    boolean mStartAnimationTriggered;
+    boolean mCanReuseTaskStackViews = true;
+
+    // Task launching
+    RecentsConfiguration mConfig;
+    Rect mSearchBarBounds = new Rect();
+    Rect mTaskStackBounds = new Rect();
+    Rect mLastTaskViewBounds = new Rect();
+    TaskViewTransform mTmpTransform = new TaskViewTransform();
+    int mStatusBarHeight;
+    int mNavBarHeight;
+    int mNavBarWidth;
+    int mTaskBarHeight;
+
+    // Header (for transition)
+    TaskViewHeader mHeaderBar;
+    final Object mHeaderBarLock = new Object();
+    TaskStackView mDummyStackView;
+
+    // Variables to keep track of if we need to start recents after binding
+    boolean mTriggeredFromAltTab;
+    long mLastToggleTime;
+
+    Bitmap mThumbnailTransitionBitmapCache;
+    Task mThumbnailTransitionBitmapCacheKey;
+
+
+    public RecentsImpl(Context context) {
+        mContext = context;
+        mSystemServicesProxy = new SystemServicesProxy(mContext);
+        mHandler = new Handler();
+        mAppWidgetHost = new RecentsAppWidgetHost(mContext, Constants.Values.App.AppWidgetHostId);
+        Resources res = mContext.getResources();
+        RecentsTaskLoader.initialize(mContext);
+        LayoutInflater inflater = LayoutInflater.from(mContext);
+
+        // Register the task stack listener
+        mTaskStackListener = new TaskStackListenerImpl(mHandler);
+        mSystemServicesProxy.registerTaskStackListener(mTaskStackListener);
+
+        // Initialize the static configuration resources
+        mConfig = RecentsConfiguration.initialize(mContext, mSystemServicesProxy);
+        mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+        mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
+        mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
+        mTaskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
+        mDummyStackView = new TaskStackView(mContext, new TaskStack());
+        mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
+                null, false);
+        reloadHeaderBarLayout(true /* tryAndBindSearchWidget */);
+
+        // When we start, preload the data associated with the previous recent tasks.
+        // We can use a new plan since the caches will be the same.
+        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+        RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
+        loader.preloadTasks(plan, true /* isTopTaskHome */);
+        RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+        launchOpts.numVisibleTasks = loader.getApplicationIconCacheSize();
+        launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
+        launchOpts.onlyLoadForCache = true;
+        loader.loadTasks(mContext, plan, launchOpts);
+    }
+
+    public void onBootCompleted() {
+        mBootCompleted = true;
+        reloadHeaderBarLayout(true /* tryAndBindSearchWidget */);
+    }
+
+    @Override
+    public void onConfigurationChanged() {
+        // Don't reuse task stack views if the configuration changes
+        mCanReuseTaskStackViews = false;
+        mConfig.updateOnConfigurationChange();
+    }
+
+    /**
+     * This is only called from the system user's Recents.  Secondary users will instead proxy their
+     * visibility change events through to the system user via
+     * {@link Recents#onBusEvent(RecentsVisibilityChangedEvent)}.
+     */
+    public void onVisibilityChanged(Context context, boolean visible) {
+        SystemUIApplication app = (SystemUIApplication) context;
+        PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
+        if (statusBar != null) {
+            statusBar.updateRecentsVisibility(visible);
+        }
+    }
+
+    /**
+     * This is only called from the system user's Recents.  Secondary users will instead proxy their
+     * visibility change events through to the system user via
+     * {@link Recents#onBusEvent(ScreenPinningRequestEvent)}.
+     */
+    public void onStartScreenPinning(Context context) {
+        SystemUIApplication app = (SystemUIApplication) context;
+        PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
+        if (statusBar != null) {
+            statusBar.showScreenPinningRequest(false);
+        }
+    }
+
+    @Override
+    public void showRecents(boolean triggeredFromAltTab) {
+        mTriggeredFromAltTab = triggeredFromAltTab;
+
+        try {
+            // Check if the top task is in the home stack, and start the recents activity
+            ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
+            MutableBoolean isTopTaskHome = new MutableBoolean(true);
+            if (topTask == null || !mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
+                startRecentsActivity(topTask, isTopTaskHome.value);
+            }
+        } catch (ActivityNotFoundException e) {
+            Console.logRawError("Failed to launch RecentAppsIntent", e);
+        }
+    }
+
+    @Override
+    public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
+        if (mBootCompleted) {
+            // Defer to the activity to handle hiding recents, if it handles it, then it must still
+            // be visible
+            Intent intent = createLocalBroadcastIntent(mContext, ACTION_HIDE_RECENTS_ACTIVITY);
+            intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
+            intent.putExtra(EXTRA_TRIGGERED_FROM_HOME_KEY, triggeredFromHomeKey);
+            mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+        }
+    }
+
+    @Override
+    public void toggleRecents() {
+        mTriggeredFromAltTab = false;
+
+        try {
+            // If the user has toggled it too quickly, then just eat up the event here (it's better
+            // than showing a janky screenshot).
+            // NOTE: Ideally, the screenshot mechanism would take the window transform into account
+            if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) {
+                return;
+            }
+
+            // If Recents is the front most activity, then we should just communicate with it
+            // directly to launch the first task or dismiss itself
+            ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
+            MutableBoolean isTopTaskHome = new MutableBoolean(true);
+            if (topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
+                // Notify recents to toggle itself
+                Intent intent = createLocalBroadcastIntent(mContext, ACTION_TOGGLE_RECENTS_ACTIVITY);
+                mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+                mLastToggleTime = SystemClock.elapsedRealtime();
+                return;
+            } else {
+                // Otherwise, start the recents activity
+                startRecentsActivity(topTask, isTopTaskHome.value);
+            }
+        } catch (ActivityNotFoundException e) {
+            Console.logRawError("Failed to launch RecentAppsIntent", e);
+        }
+    }
+
+    @Override
+    public void preloadRecents() {
+        // Preload only the raw task list into a new load plan (which will be consumed by the
+        // RecentsActivity) only if there is a task to animate to.
+        ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
+        MutableBoolean topTaskHome = new MutableBoolean(true);
+        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+        sInstanceLoadPlan = loader.createLoadPlan(mContext);
+        if (topTask != null && !mSystemServicesProxy.isRecentsTopMost(topTask, topTaskHome)) {
+            sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);
+            loader.preloadTasks(sInstanceLoadPlan, topTaskHome.value);
+            TaskStack stack = sInstanceLoadPlan.getTaskStack();
+            if (stack.getTaskCount() > 0) {
+                preCacheThumbnailTransitionBitmapAsync(topTask, stack, mDummyStackView);
+            }
+        }
+    }
+
+    @Override
+    public void cancelPreloadingRecents() {
+        // Do nothing
+    }
+
+    public void showRelativeAffiliatedTask(boolean showNextTask) {
+        // Return early if there is no focused stack
+        int focusedStackId = mSystemServicesProxy.getFocusedStack();
+        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+        RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
+        loader.preloadTasks(plan, true /* isTopTaskHome */);
+        TaskStack focusedStack = plan.getTaskStack();
+
+        // Return early if there are no tasks in the focused stack
+        if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
+
+        ActivityManager.RunningTaskInfo runningTask = mSystemServicesProxy.getTopMostTask();
+        // Return early if there is no running task (can't determine affiliated tasks in this case)
+        if (runningTask == null) return;
+        // Return early if the running task is in the home stack (optimization)
+        if (mSystemServicesProxy.isInHomeStack(runningTask.id)) return;
+
+        // Find the task in the recents list
+        ArrayList<Task> tasks = focusedStack.getTasks();
+        Task toTask = null;
+        ActivityOptions launchOpts = null;
+        int taskCount = tasks.size();
+        int numAffiliatedTasks = 0;
+        for (int i = 0; i < taskCount; i++) {
+            Task task = tasks.get(i);
+            if (task.key.id == runningTask.id) {
+                TaskGrouping group = task.group;
+                Task.TaskKey toTaskKey;
+                if (showNextTask) {
+                    toTaskKey = group.getNextTaskInGroup(task);
+                    launchOpts = ActivityOptions.makeCustomAnimation(mContext,
+                            R.anim.recents_launch_next_affiliated_task_target,
+                            R.anim.recents_launch_next_affiliated_task_source);
+                } else {
+                    toTaskKey = group.getPrevTaskInGroup(task);
+                    launchOpts = ActivityOptions.makeCustomAnimation(mContext,
+                            R.anim.recents_launch_prev_affiliated_task_target,
+                            R.anim.recents_launch_prev_affiliated_task_source);
+                }
+                if (toTaskKey != null) {
+                    toTask = focusedStack.findTaskWithId(toTaskKey.id);
+                }
+                numAffiliatedTasks = group.getTaskCount();
+                break;
+            }
+        }
+
+        // Return early if there is no next task
+        if (toTask == null) {
+            if (numAffiliatedTasks > 1) {
+                if (showNextTask) {
+                    mSystemServicesProxy.startInPlaceAnimationOnFrontMostApplication(
+                            ActivityOptions.makeCustomInPlaceAnimation(mContext,
+                                    R.anim.recents_launch_next_affiliated_task_bounce));
+                } else {
+                    mSystemServicesProxy.startInPlaceAnimationOnFrontMostApplication(
+                            ActivityOptions.makeCustomInPlaceAnimation(mContext,
+                                    R.anim.recents_launch_prev_affiliated_task_bounce));
+                }
+            }
+            return;
+        }
+
+        // Keep track of actually launched affiliated tasks
+        MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
+
+        // Launch the task
+        if (toTask.isActive) {
+            // Bring an active task to the foreground
+            mSystemServicesProxy.moveTaskToFront(toTask.key.id, launchOpts);
+        } else {
+            mSystemServicesProxy.startActivityFromRecents(mContext, toTask.key.id,
+                    toTask.activityLabel, launchOpts);
+        }
+    }
+
+    public void showNextAffiliatedTask() {
+        // Keep track of when the affiliated task is triggered
+        MetricsLogger.count(mContext, "overview_affiliated_task_next", 1);
+        showRelativeAffiliatedTask(true);
+    }
+
+    public void showPrevAffiliatedTask() {
+        // Keep track of when the affiliated task is triggered
+        MetricsLogger.count(mContext, "overview_affiliated_task_prev", 1);
+        showRelativeAffiliatedTask(false);
+    }
+
+    /**
+     * Returns the preloaded load plan and invalidates it.
+     */
+    public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
+        RecentsTaskLoadPlan plan = sInstanceLoadPlan;
+        sInstanceLoadPlan = null;
+        return plan;
+    }
+
+    /**
+     * Prepares the header bar layout for the next transition, if the task view bounds has changed
+     * since the last call, it will attempt to re-measure and layout the header bar to the new size.
+     *
+     * @param tryAndBindSearchWidget if set, will attempt to fetch and bind the search widget if one
+     *                               is not already bound (can be expensive)
+     */
+    private void reloadHeaderBarLayout(boolean tryAndBindSearchWidget) {
+        Rect windowRect = mSystemServicesProxy.getWindowRect();
+
+        // Update the configuration for the current state
+        mConfig.update(mContext, mSystemServicesProxy, mSystemServicesProxy.getWindowRect());
+
+        if (tryAndBindSearchWidget) {
+            // Try and pre-emptively bind the search widget on startup to ensure that we
+            // have the right thumbnail bounds to animate to.
+            // Note: We have to reload the widget id before we get the task stack bounds below
+            if (mSystemServicesProxy.getOrBindSearchAppWidget(mContext, mAppWidgetHost) != null) {
+                mConfig.getSearchBarBounds(windowRect,
+                        mStatusBarHeight, mSearchBarBounds);
+            }
+        }
+        Rect systemInsets = new Rect(0, mStatusBarHeight,
+                (mConfig.hasTransposedNavBar ? mNavBarWidth : 0),
+                (mConfig.hasTransposedNavBar ? 0 : mNavBarHeight));
+        mConfig.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right,
+                mSearchBarBounds, mTaskStackBounds);
+
+        // Rebind the header bar and draw it for the transition
+        TaskStackViewLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
+        Rect taskStackBounds = new Rect(mTaskStackBounds);
+        algo.setSystemInsets(systemInsets);
+        algo.computeRects(windowRect.width(), windowRect.height(), taskStackBounds);
+        Rect taskViewBounds = algo.getUntransformedTaskViewBounds();
+        if (!taskViewBounds.equals(mLastTaskViewBounds)) {
+            mLastTaskViewBounds.set(taskViewBounds);
+
+            int taskViewWidth = taskViewBounds.width();
+            synchronized (mHeaderBarLock) {
+                mHeaderBar.measure(
+                    View.MeasureSpec.makeMeasureSpec(taskViewWidth, View.MeasureSpec.EXACTLY),
+                    View.MeasureSpec.makeMeasureSpec(mTaskBarHeight, View.MeasureSpec.EXACTLY));
+                mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
+            }
+        }
+    }
+
+    /**
+     * Preloads the icon of a task.
+     */
+    private void preloadIcon(ActivityManager.RunningTaskInfo task) {
+
+        // Ensure that we load the running task's icon
+        RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+        launchOpts.runningTaskId = task.id;
+        launchOpts.loadThumbnails = false;
+        launchOpts.onlyLoadForCache = true;
+        RecentsTaskLoader.getInstance().loadTasks(mContext, sInstanceLoadPlan, launchOpts);
+    }
+
+    /**
+     * Caches the header thumbnail used for a window animation asynchronously into
+     * {@link #mThumbnailTransitionBitmapCache}.
+     */
+    private void preCacheThumbnailTransitionBitmapAsync(ActivityManager.RunningTaskInfo topTask,
+            TaskStack stack, TaskStackView stackView) {
+        preloadIcon(topTask);
+
+        // Update the header bar if necessary
+        reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
+
+        // Update the destination rect
+        mDummyStackView.updateMinMaxScrollForStack(stack);
+        final Task toTask = new Task();
+        final TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
+                topTask.id, toTask);
+        new AsyncTask<Void, Void, Bitmap>() {
+            @Override
+            protected Bitmap doInBackground(Void... params) {
+                return drawThumbnailTransitionBitmap(toTask, toTransform);
+            }
+
+            @Override
+            protected void onPostExecute(Bitmap bitmap) {
+                mThumbnailTransitionBitmapCache = bitmap;
+                mThumbnailTransitionBitmapCacheKey = toTask;
+            }
+        }.execute();
+    }
+
+    /**
+     * Creates the activity options for a unknown state->recents transition.
+     */
+    private ActivityOptions getUnknownTransitionActivityOptions() {
+        mStartAnimationTriggered = false;
+        return ActivityOptions.makeCustomAnimation(mContext,
+                R.anim.recents_from_unknown_enter,
+                R.anim.recents_from_unknown_exit,
+                mHandler, this);
+    }
+
+    /**
+     * Creates the activity options for a home->recents transition.
+     */
+    private ActivityOptions getHomeTransitionActivityOptions(boolean fromSearchHome) {
+        mStartAnimationTriggered = false;
+        if (fromSearchHome) {
+            return ActivityOptions.makeCustomAnimation(mContext,
+                    R.anim.recents_from_search_launcher_enter,
+                    R.anim.recents_from_search_launcher_exit,
+                    mHandler, this);
+        }
+        return ActivityOptions.makeCustomAnimation(mContext,
+                R.anim.recents_from_launcher_enter,
+                R.anim.recents_from_launcher_exit,
+                mHandler, this);
+    }
+
+    /**
+     * Creates the activity options for an app->recents transition.
+     */
+    private ActivityOptions getThumbnailTransitionActivityOptions(
+            ActivityManager.RunningTaskInfo topTask, TaskStack stack, TaskStackView stackView) {
+
+        // Update the destination rect
+        Task toTask = new Task();
+        TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
+                topTask.id, toTask);
+        Rect toTaskRect = toTransform.rect;
+        Bitmap thumbnail;
+        if (mThumbnailTransitionBitmapCacheKey != null
+                && mThumbnailTransitionBitmapCacheKey.key != null
+                && mThumbnailTransitionBitmapCacheKey.key.equals(toTask.key)) {
+            thumbnail = mThumbnailTransitionBitmapCache;
+            mThumbnailTransitionBitmapCacheKey = null;
+            mThumbnailTransitionBitmapCache = null;
+        } else {
+            preloadIcon(topTask);
+            thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform);
+        }
+        if (thumbnail != null) {
+            mStartAnimationTriggered = false;
+            return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
+                    thumbnail, toTaskRect.left, toTaskRect.top, toTaskRect.width(),
+                    toTaskRect.height(), mHandler, this);
+        }
+
+        // If both the screenshot and thumbnail fails, then just fall back to the default transition
+        return getUnknownTransitionActivityOptions();
+    }
+
+    /**
+     * Returns the transition rect for the given task id.
+     */
+    private TaskViewTransform getThumbnailTransitionTransform(TaskStack stack,
+            TaskStackView stackView, int runningTaskId, Task runningTaskOut) {
+        // Find the running task in the TaskStack
+        Task task = null;
+        ArrayList<Task> tasks = stack.getTasks();
+        if (runningTaskId != -1) {
+            // Otherwise, try and find the task with the
+            int taskCount = tasks.size();
+            for (int i = taskCount - 1; i >= 0; i--) {
+                Task t = tasks.get(i);
+                if (t.key.id == runningTaskId) {
+                    task = t;
+                    runningTaskOut.copyFrom(t);
+                    break;
+                }
+            }
+        }
+        if (task == null) {
+            // If no task is specified or we can not find the task just use the front most one
+            task = tasks.get(tasks.size() - 1);
+            runningTaskOut.copyFrom(task);
+        }
+
+        // Get the transform for the running task
+        stackView.getScroller().setStackScrollToInitialState();
+        mTmpTransform = stackView.getStackAlgorithm().getStackTransform(task,
+                stackView.getScroller().getStackScroll(), mTmpTransform, null);
+        return mTmpTransform;
+    }
+
+    /**
+     * Draws the header of a task used for the window animation into a bitmap.
+     */
+    private Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform) {
+        if (toTransform != null && toTask.key != null) {
+            Bitmap thumbnail;
+            synchronized (mHeaderBarLock) {
+                int toHeaderWidth = (int) (mHeaderBar.getMeasuredWidth() * toTransform.scale);
+                int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
+                thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
+                        Bitmap.Config.ARGB_8888);
+                if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
+                    thumbnail.eraseColor(0xFFff0000);
+                } else {
+                    Canvas c = new Canvas(thumbnail);
+                    c.scale(toTransform.scale, toTransform.scale);
+                    mHeaderBar.rebindToTask(toTask);
+                    mHeaderBar.draw(c);
+                    c.setBitmap(null);
+                }
+            }
+            return thumbnail.createAshmemBitmap();
+        }
+        return null;
+    }
+
+    /**
+     * Shows the recents activity
+     */
+    private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
+            boolean isTopTaskHome) {
+        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+
+        // Update the header bar if necessary
+        reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
+
+        if (sInstanceLoadPlan == null) {
+            // Create a new load plan if onPreloadRecents() was never triggered
+            sInstanceLoadPlan = loader.createLoadPlan(mContext);
+        }
+
+        if (!sInstanceLoadPlan.hasTasks()) {
+            loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
+        }
+        TaskStack stack = sInstanceLoadPlan.getTaskStack();
+
+        // Prepare the dummy stack for the transition
+        mDummyStackView.updateMinMaxScrollForStack(stack);
+        TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
+                mDummyStackView.computeStackVisibilityReport();
+        boolean hasRecentTasks = stack.getTaskCount() > 0;
+        boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
+
+        if (useThumbnailTransition) {
+            // Try starting with a thumbnail transition
+            ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
+                    mDummyStackView);
+            if (opts != null) {
+                startRecentsActivity(topTask, opts, false /* fromHome */,
+                        false /* fromSearchHome */, true /* fromThumbnail */, stackVr);
+            } else {
+                // Fall through below to the non-thumbnail transition
+                useThumbnailTransition = false;
+            }
+        }
+
+        if (!useThumbnailTransition) {
+            // If there is no thumbnail transition, but is launching from home into recents, then
+            // use a quick home transition and do the animation from home
+            if (hasRecentTasks) {
+                String homeActivityPackage = mSystemServicesProxy.getHomeActivityPackageName();
+                String searchWidgetPackage =
+                        Prefs.getString(mContext, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE, null);
+
+                // Determine whether we are coming from a search owned home activity
+                boolean fromSearchHome = (homeActivityPackage != null) &&
+                        homeActivityPackage.equals(searchWidgetPackage);
+                ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
+                startRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
+                        false /* fromThumbnail */, stackVr);
+            } else {
+                // Otherwise we do the normal fade from an unknown source
+                ActivityOptions opts = getUnknownTransitionActivityOptions();
+                startRecentsActivity(topTask, opts, true /* fromHome */,
+                        false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
+            }
+        }
+        mLastToggleTime = SystemClock.elapsedRealtime();
+    }
+
+    /**
+     * Starts the recents activity.
+     */
+    private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
+              ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
+              TaskStackViewLayoutAlgorithm.VisibilityReport vr) {
+        // Update the configuration based on the launch options
+        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+        launchState.launchedFromHome = fromSearchHome || fromHome;
+        launchState.launchedFromSearchHome = fromSearchHome;
+        launchState.launchedFromAppWithThumbnail = fromThumbnail;
+        launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
+        launchState.launchedWithAltTab = mTriggeredFromAltTab;
+        launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
+        launchState.launchedNumVisibleTasks = vr.numVisibleTasks;
+        launchState.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
+        launchState.launchedHasConfigurationChanged = false;
+
+        Intent intent = new Intent();
+        intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+        if (opts != null) {
+            mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
+        } else {
+            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+        }
+        mCanReuseTaskStackViews = true;
+    }
+
+    /**
+     * Creates a new broadcast intent to send to the Recents activity.
+     * TODO: Use EventBus
+     */
+    private Intent createLocalBroadcastIntent(Context context, String action) {
+        Intent intent = new Intent(action);
+        intent.setPackage(context.getPackageName());
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
+                Intent.FLAG_RECEIVER_FOREGROUND);
+        return intent;
+    }
+
+    /**** OnAnimationStartedListener Implementation ****/
+
+    @Override
+    public void onAnimationStarted() {
+        // Notify recents to start the enter animation
+        // TODO: Use EventBus
+        if (!mStartAnimationTriggered) {
+            // There can be a race condition between the start animation callback and
+            // the start of the new activity (where we register the receiver that listens
+            // to this broadcast, so we add our own receiver and if that gets called, then
+            // we know the activity has not yet started and we can retry sending the broadcast.
+            BroadcastReceiver fallbackReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (getResultCode() == Activity.RESULT_OK) {
+                        mStartAnimationTriggered = true;
+                        return;
+                    }
+
+                    // Schedule for the broadcast to be sent again after some time
+                    mHandler.postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            onAnimationStarted();
+                        }
+                    }, 25);
+                }
+            };
+
+            // Send the broadcast to notify Recents that the animation has started
+            Intent intent = createLocalBroadcastIntent(mContext, ACTION_START_ENTER_ANIMATION);
+            mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
+                    fallbackReceiver, null, Activity.RESULT_CANCELED, null, null);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
new file mode 100644
index 0000000..fb21500
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+
+/**
+ * An implementation of the system user's Recents interface to be called remotely by secondary
+ * users.
+ */
+public class RecentsSystemUser extends IRecentsSystemUserCallbacks.Stub {
+
+    private static final String TAG = "RecentsSystemUser";
+
+    private Context mContext;
+    private RecentsImpl mImpl;
+    private final SparseArray<IRecentsNonSystemUserCallbacks> mNonSystemUserRecents =
+            new SparseArray<>();
+
+    public RecentsSystemUser(Context context, RecentsImpl impl) {
+        mContext = context;
+        mImpl = impl;
+    }
+
+    @Override
+    public void registerNonSystemUserCallbacks(final IBinder nonSystemUserCallbacks, int userId) {
+        try {
+            final IRecentsNonSystemUserCallbacks callback =
+                    IRecentsNonSystemUserCallbacks.Stub.asInterface(nonSystemUserCallbacks);
+            nonSystemUserCallbacks.linkToDeath(new IBinder.DeathRecipient() {
+                @Override
+                public void binderDied() {
+                    mNonSystemUserRecents.removeAt(mNonSystemUserRecents.indexOfValue(callback));
+                }
+            }, 0);
+            mNonSystemUserRecents.put(userId, callback);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to register NonSystemUserCallbacks", e);
+        }
+    }
+
+    public IRecentsNonSystemUserCallbacks getNonSystemUserRecentsForUser(int userId) {
+        return mNonSystemUserRecents.get(userId);
+    }
+
+    @Override
+    public void updateRecentsVisibility(boolean visible) {
+        mImpl.onVisibilityChanged(mContext, visible);
+    }
+
+    @Override
+    public void startScreenPinning() {
+        mImpl.onStartScreenPinning(mContext);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java
new file mode 100644
index 0000000..39d0d59
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+import com.android.systemui.SystemUIApplication;
+
+/**
+ * A strictly system-user service that is started by the secondary user's Recents (with a limited
+ * lifespan), to get the interface that the secondary user's Recents can call through to the system
+ * user's Recents.
+ */
+public class RecentsSystemUserService extends Service {
+
+    private static final String TAG = "RecentsSystemUserService";
+    private static final boolean DEBUG = false;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        SystemUIApplication app = (SystemUIApplication) getApplication();
+        Recents recents = app.getComponent(Recents.class);
+        if (DEBUG) {
+            Log.d(TAG, "onBind: " + recents);
+        }
+        if (recents != null) {
+            return recents.getSystemUserCallbacks();
+        }
+        return null;
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsUserEventProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsUserEventProxyReceiver.java
deleted file mode 100644
index 5eefbc7..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsUserEventProxyReceiver.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-/**
- * A proxy for Recents events which happens strictly for non-owner users.
- */
-public class RecentsUserEventProxyReceiver extends BroadcastReceiver {
-    final public static String ACTION_PROXY_SHOW_RECENTS_TO_USER =
-            "com.android.systemui.recents.action.SHOW_RECENTS_FOR_USER";
-    final public static String ACTION_PROXY_HIDE_RECENTS_TO_USER =
-            "com.android.systemui.recents.action.HIDE_RECENTS_FOR_USER";
-    final public static String ACTION_PROXY_TOGGLE_RECENTS_TO_USER =
-            "com.android.systemui.recents.action.TOGGLE_RECENTS_FOR_USER";
-    final public static String ACTION_PROXY_PRELOAD_RECENTS_TO_USER =
-            "com.android.systemui.recents.action.PRELOAD_RECENTS_FOR_USER";
-    final public static String ACTION_PROXY_CONFIG_CHANGE_TO_USER =
-            "com.android.systemui.recents.action.CONFIG_CHANGED_FOR_USER";
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        Recents recents = Recents.getInstanceAndStartIfNeeded(context);
-        switch (intent.getAction()) {
-            case ACTION_PROXY_SHOW_RECENTS_TO_USER: {
-                boolean triggeredFromAltTab = intent.getBooleanExtra(
-                        Recents.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
-                recents.showRecentsInternal(triggeredFromAltTab);
-                break;
-            }
-            case ACTION_PROXY_HIDE_RECENTS_TO_USER: {
-                boolean triggeredFromAltTab = intent.getBooleanExtra(
-                        Recents.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
-                boolean triggeredFromHome = intent.getBooleanExtra(
-                        Recents.EXTRA_TRIGGERED_FROM_HOME_KEY, false);
-                recents.hideRecentsInternal(triggeredFromAltTab, triggeredFromHome);
-                break;
-            }
-            case ACTION_PROXY_TOGGLE_RECENTS_TO_USER:
-                recents.toggleRecentsInternal();
-                break;
-            case ACTION_PROXY_PRELOAD_RECENTS_TO_USER:
-                recents.preloadRecentsInternal();
-                break;
-            case ACTION_PROXY_CONFIG_CHANGE_TO_USER:
-                recents.configurationChanged();
-                break;
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java
new file mode 100644
index 0000000..898d1fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.component;
+
+import android.content.Context;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+
+/**
+ * This is sent when the visibility of the RecentsActivity for the current user changes.
+ */
+public class RecentsVisibilityChangedEvent extends EventBus.Event {
+
+    public final Context applicationContext;
+    public final SystemServicesProxy systemServicesProxy;
+    public final boolean visible;
+
+    public RecentsVisibilityChangedEvent(Context context, SystemServicesProxy systemServicesProxy,
+            boolean visible) {
+        this.applicationContext = context.getApplicationContext();
+        this.systemServicesProxy = systemServicesProxy;
+        this.visible = visible;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
new file mode 100644
index 0000000..5cb4ccf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.component;
+
+import android.content.Context;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+
+/**
+ * This is sent when we want to start screen pinning.
+ */
+public class ScreenPinningRequestEvent extends EventBus.Event {
+
+    public final Context applicationContext;
+    public final SystemServicesProxy systemServicesProxy;
+
+    public ScreenPinningRequestEvent(Context context, SystemServicesProxy systemServicesProxy) {
+        this.applicationContext = context.getApplicationContext();
+        this.systemServicesProxy = systemServicesProxy;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 568d2b1..dab2c65 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -16,9 +16,6 @@
 
 package com.android.systemui.recents.misc;
 
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.INVALID_STACK_ID;
-
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.ActivityOptions;
@@ -55,6 +52,7 @@
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.MutableBoolean;
@@ -66,7 +64,7 @@
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsImpl;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -74,6 +72,9 @@
 import java.util.List;
 import java.util.Random;
 
+import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.INVALID_STACK_ID;
+
 /**
  * Acts as a shim around the real system services that we need to access data from, and provides
  * a point of injection when testing UI.
@@ -100,6 +101,7 @@
     IPackageManager mIpm;
     AssistUtils mAssistUtils;
     WindowManager mWm;
+    UserManager mUm;
     Display mDisplay;
     String mRecentsPackage;
     ComponentName mAssistComponent;
@@ -122,6 +124,7 @@
         mIpm = AppGlobals.getPackageManager();
         mAssistUtils = new AssistUtils(context);
         mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        mUm = UserManager.get(context);
         mDisplay = mWm.getDefaultDisplay();
         mRecentsPackage = context.getPackageName();
         mBgThreadHandler = new Handler(sBgThread.getLooper());
@@ -244,8 +247,8 @@
             ComponentName topActivity = topTask.topActivity;
 
             // Check if the front most activity is recents
-            if (topActivity.getPackageName().equals(Recents.sRecentsPackage) &&
-                    topActivity.getClassName().equals(Recents.sRecentsActivity)) {
+            if (topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE) &&
+                    topActivity.getClassName().equals(RecentsImpl.RECENTS_ACTIVITY)) {
                 if (isHomeTopMost != null) {
                     isHomeTopMost.value = false;
                 }
@@ -552,12 +555,27 @@
     }
 
     /**
-     * Returns whether the foreground user is the owner.
+     * Returns whether the provided {@param userId} represents the system user.
      */
-    public boolean isForegroundUserSystem() {
-        if (mAm == null) return false;
+    public boolean isSystemUser(int userId) {
+        return userId == UserHandle.USER_SYSTEM;
+    }
 
-        return mAm.getCurrentUser() == UserHandle.USER_SYSTEM;
+    /**
+     * Returns the current user id.
+     */
+    public int getCurrentUser() {
+        if (mAm == null) return 0;
+
+        return mAm.getCurrentUser();
+    }
+
+    /**
+     * Returns the processes user id.
+     */
+    public int getProcessUser() {
+        if (mUm == null) return 0;
+        return mUm.getUserHandle();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 4322f1a..3673131 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -395,8 +395,10 @@
 
         // The user intentionally tapped on the background, which is like a tap on the "desktop".
         // Hide recents and transition to the launcher.
+        /* TODO: Use EventBus for this later
         Recents recents = Recents.getInstanceAndStartIfNeeded(mSv.getContext());
-        recents.hideRecents(false /* altTab */, true /* homeKey */);
+        recents.hideRecents(false, true);
+        */
     }
 
     /** Handles generic motion events */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 540b9d0..fa9c4bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -109,6 +109,14 @@
         }
     };
 
+    private Runnable mRemoveCastIconRunnable = new Runnable() {
+        @Override
+        public void run() {
+            if (DEBUG) Log.v(TAG, "updateCast: hiding icon NOW");
+            mService.setIconVisibility(SLOT_CAST, false);
+        }
+    };
+
     public PhoneStatusBarPolicy(Context context, CastController cast, HotspotController hotspot,
             UserInfoController userInfoController, BluetoothController bluetooth) {
         mContext = context;
@@ -328,11 +336,17 @@
             }
         }
         if (DEBUG) Log.v(TAG, "updateCast: isCasting: " + isCasting);
+        mHandler.removeCallbacks(mRemoveCastIconRunnable);
         if (isCasting) {
             mService.setIcon(SLOT_CAST, R.drawable.stat_sys_cast, 0,
                     mContext.getString(R.string.accessibility_casting));
+            mService.setIconVisibility(SLOT_CAST, true);
+        } else {
+            // don't turn off the screen-record icon for a few seconds, just to make sure the user
+            // has seen it
+            if (DEBUG) Log.v(TAG, "updateCast: hiding icon in 3 sec...");
+            mHandler.postDelayed(mRemoveCastIconRunnable, 3000);
         }
-        mService.setIconVisibility(SLOT_CAST, isCasting);
     }
 
     private void profileChanged(int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index 01f0667..662dbd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -20,7 +20,9 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Rect;
 import android.graphics.drawable.Animatable;
+import android.graphics.drawable.RippleDrawable;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
@@ -28,6 +30,7 @@
 import android.widget.Switch;
 import android.widget.TextView;
 import android.widget.Toast;
+
 import com.android.systemui.R;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QSTile;
@@ -86,6 +89,20 @@
         mQsDetailHeaderTitle = (TextView) mQsDetailHeader.findViewById(android.R.id.title);
         mQsDetailHeaderSwitch = (Switch) mQsDetailHeader.findViewById(android.R.id.toggle);
         mQsDetailHeaderProgress = (ImageView) findViewById(R.id.qs_detail_header_progress);
+
+        // RenderThread is doing more harm than good when touching the header (to expand quick
+        // settings), so disable it for this view
+        ((RippleDrawable) getBackground()).setForceSoftware(true);
+        ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
+
+        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) {
+                setClipBounds(new Rect(getPaddingLeft(), 0, getWidth() - getPaddingRight(),
+                        getHeight()));
+            }
+        });
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 56f6036..a515f23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -602,6 +602,13 @@
 
         @Override
         public int compareTo(HeadsUpEntry o) {
+            boolean selfFullscreen = hasFullScreenIntent(entry);
+            boolean otherFullscreen = hasFullScreenIntent(o.entry);
+            if (selfFullscreen && !otherFullscreen) {
+                return -1;
+            } else if (!selfFullscreen && otherFullscreen) {
+                return 1;
+            }
             return postTime < o.postTime ? 1
                     : postTime == o.postTime ? entry.key.compareTo(o.entry.key)
                             : -1;
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index d6d17cb..fdc2543 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -22,7 +22,9 @@
 LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
 
 LOCAL_AAPT_FLAGS := --auto-add-overlay --extra-packages com.android.systemui:com.android.keyguard
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    $(call all-Iaidl-files-under, src) \
     $(call all-java-files-under, ../src) \
     $(call all-proto-files-under, ../src) \
     src/com/android/systemui/EventLogTags.logtags
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
new file mode 120000
index 0000000..0ea3e91
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
@@ -0,0 +1 @@
+../../../../../../src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
new file mode 120000
index 0000000..b1a0963
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
@@ -0,0 +1 @@
+../../../../../../src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
index 7dff1a8..28121b4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
@@ -17,53 +17,146 @@
 package com.android.server.accessibility;
 
 import android.content.Context;
+import android.gesture.Gesture;
+import android.gesture.GestureLibraries;
+import android.gesture.GestureLibrary;
+import android.gesture.GesturePoint;
+import android.gesture.GestureStore;
+import android.gesture.GestureStroke;
+import android.gesture.Prediction;
+import android.util.Slog;
 import android.view.GestureDetector;
 import android.view.MotionEvent;
 
+import com.android.internal.R;
+
+import java.util.ArrayList;
+
 /**
  * This class handles gesture detection for the Touch Explorer.  It collects
  * touch events, and sends events to mListener as gestures are recognized.
  */
 class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListener {
-    private final GestureDetector mGestureDetector;
+
+    private static final boolean DEBUG = false;
+
+    // Tag for logging received events.
+    private static final String LOG_TAG = "AccessibilityGestureDetector";
+
+    public interface Listener {
+        public void onDoubleTapAndHold(MotionEvent event, int policyFlags);
+        public boolean onDoubleTap(MotionEvent event, int policyFlags);
+        public boolean onGesture(int gestureId);
+    }
+
     private final Listener mListener;
+    private final GestureDetector mGestureDetector;
+
+    // The library for gesture detection.
+    private final GestureLibrary mGestureLibrary;
+
+    // Indicates that a single tap has occurred.
     private boolean mFirstTapDetected;
+
+    // Indicates that the down event of a double tap has occured.
     private boolean mDoubleTapDetected;
+
+    // Indicates that motion events are being collected to match a gesture.
+    private boolean mRecognizingGesture;
+
+    // Policy flags of the previous event.
     private int mPolicyFlags;
 
+    // The X of the previous event.
+    private float mPreviousX;
+
+    // The Y of the previous event.
+    private float mPreviousY;
+
+    // Buffer for storing points for gesture detection.
+    private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
+
+    // The minimal delta between moves to add a gesture point.
+    private static final int TOUCH_TOLERANCE = 3;
+
+    // The minimal score for accepting a predicted gesture.
+    private static final float MIN_PREDICTION_SCORE = 2.0f;
+
     AccessibilityGestureDetector(Context context, Listener listener) {
         mListener = listener;
 
         mGestureDetector = new GestureDetector(context, this);
         mGestureDetector.setOnDoubleTapListener(this);
+
+        mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures);
+        mGestureLibrary.setOrientationStyle(8 /* GestureStore.ORIENTATION_SENSITIVE_8 */);
+        mGestureLibrary.setSequenceType(GestureStore.SEQUENCE_SENSITIVE);
+        mGestureLibrary.load();
     }
 
-    public void onMotionEvent(MotionEvent event, int policyFlags) {
+    public boolean onMotionEvent(MotionEvent event, int policyFlags) {
+        final float x = event.getX();
+        final float y = event.getY();
+
         mPolicyFlags = policyFlags;
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
                 mDoubleTapDetected = false;
+                mRecognizingGesture = true;
+                mPreviousX = x;
+                mPreviousY = y;
+                mStrokeBuffer.clear();
+                mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                if (mRecognizingGesture) {
+                    final float dX = Math.abs(x - mPreviousX);
+                    final float dY = Math.abs(y - mPreviousY);
+                    if (dX >= TOUCH_TOLERANCE || dY >= TOUCH_TOLERANCE) {
+                        mPreviousX = x;
+                        mPreviousY = y;
+                        mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
+                    }
+                }
                 break;
 
             case MotionEvent.ACTION_UP:
-                maybeFinishDoubleTap(event, policyFlags);
+                if (maybeFinishDoubleTap(event, policyFlags)) {
+                    return true;
+                }
+                if (mRecognizingGesture) {
+                    mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
+
+                    if (recognizeGesture()) {
+                        return true;
+                    }
+                }
                 break;
         }
-        mGestureDetector.onTouchEvent(event);
+
+        if (!mRecognizingGesture) {
+            return false;
+        }
+
+        // Pass the event on to the standard gesture detector.
+        return mGestureDetector.onTouchEvent(event);
     }
 
     public void clear() {
         mFirstTapDetected = false;
         mDoubleTapDetected = false;
+        cancelGesture();
+        mStrokeBuffer.clear();
     }
 
     public boolean firstTapDetected() {
         return mFirstTapDetected;
     }
 
-    @Override
-    public boolean onDown(MotionEvent event) {
-        return true;
+    public void cancelGesture() {
+        mRecognizingGesture = false;
+        mStrokeBuffer.clear();
     }
 
     @Override
@@ -101,18 +194,39 @@
         mListener.onDoubleTapAndHold(event, policyFlags);
     }
 
-    private void maybeFinishDoubleTap(MotionEvent event, int policyFlags) {
+    private boolean maybeFinishDoubleTap(MotionEvent event, int policyFlags) {
         if (!mDoubleTapDetected) {
-            return;
+            return false;
         }
 
         clear();
 
-        mListener.onDoubleTap(event, policyFlags);
+        return mListener.onDoubleTap(event, policyFlags);
     }
 
-    public interface Listener {
-        public void onDoubleTapAndHold(MotionEvent event, int policyFlags);
-        public void onDoubleTap(MotionEvent event, int policyFlags);
+    private boolean recognizeGesture() {
+        Gesture gesture = new Gesture();
+        gesture.addStroke(new GestureStroke(mStrokeBuffer));
+
+        ArrayList<Prediction> predictions = mGestureLibrary.recognize(gesture);
+        if (!predictions.isEmpty()) {
+            Prediction bestPrediction = predictions.get(0);
+            if (bestPrediction.score >= MIN_PREDICTION_SCORE) {
+                if (DEBUG) {
+                    Slog.i(LOG_TAG, "gesture: " + bestPrediction.name + " score: "
+                            + bestPrediction.score);
+                }
+                try {
+                    final int gestureId = Integer.parseInt(bestPrediction.name);
+                    if (mListener.onGesture(gestureId)) {
+                        return true;
+                    }
+                } catch (NumberFormatException nfe) {
+                    Slog.w(LOG_TAG, "Non numeric gesture id:" + bestPrediction.name);
+                }
+            }
+        }
+
+        return false;
     }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index 954536f..ca30349 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -17,13 +17,6 @@
 package com.android.server.accessibility;
 
 import android.content.Context;
-import android.gesture.Gesture;
-import android.gesture.GestureLibraries;
-import android.gesture.GestureLibrary;
-import android.gesture.GesturePoint;
-import android.gesture.GestureStore;
-import android.gesture.GestureStroke;
-import android.gesture.Prediction;
 import android.graphics.Point;
 import android.os.Handler;
 import android.util.Slog;
@@ -38,8 +31,6 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 
-import com.android.internal.R;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -167,24 +158,6 @@
     // Context in which this explorer operates.
     private final Context mContext;
 
-    // The X of the previous event.
-    private float mPreviousX;
-
-    // The Y of the previous event.
-    private float mPreviousY;
-
-    // Buffer for storing points for gesture detection.
-    private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
-
-    // The minimal delta between moves to add a gesture point.
-    private static final int TOUCH_TOLERANCE = 3;
-
-    // The minimal score for accepting a predicted gesture.
-    private static final float MIN_PREDICTION_SCORE = 2.0f;
-
-    // The library for gesture detection.
-    private GestureLibrary mGestureLibrary;
-
     // The long pressing pointer id if coordinate remapping is needed.
     private int mLongPressingPointerId = -1;
 
@@ -215,10 +188,6 @@
         mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
         mHandler = new Handler(context.getMainLooper());
         mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed();
-        mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures);
-        mGestureLibrary.setOrientationStyle(8);
-        mGestureLibrary.setSequenceType(GestureStore.SEQUENCE_SENSITIVE);
-        mGestureLibrary.load();
         mSendHoverEnterAndMoveDelayed = new SendHoverEnterAndMoveDelayed();
         mSendHoverExitDelayed = new SendHoverExitDelayed();
         mSendTouchExplorationEndDelayed = new SendAccessibilityEventDelayed(
@@ -274,8 +243,7 @@
                 sendUpForInjectedDownPointers(event, policyFlags);
             } break;
             case STATE_GESTURE_DETECTING: {
-                // Clear the current stroke.
-                mStrokeBuffer.clear();
+                // No state specific cleanup required.
             } break;
         }
         // Remove all pending callbacks.
@@ -321,6 +289,11 @@
 
         mReceivedPointerTracker.onMotionEvent(rawEvent);
 
+        if (mGestureDetector.onMotionEvent(event, policyFlags)) {
+            // Event was handled by the gesture detector.
+            return;
+        }
+
         switch(mCurrentState) {
             case STATE_TOUCH_EXPLORING: {
                 handleMotionEventStateTouchExploring(event, rawEvent, policyFlags);
@@ -389,6 +362,11 @@
 
     @Override
     public void onDoubleTapAndHold(MotionEvent event, int policyFlags) {
+        // Ignore the event if we aren't touch exploring.
+        if (mCurrentState != STATE_TOUCH_EXPLORING) {
+            return;
+        }
+
         // Pointers should not be zero when running this command.
         if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) {
             return;
@@ -415,10 +393,10 @@
     }
 
     @Override
-    public void onDoubleTap(MotionEvent event, int policyFlags) {
-        // This should never be called when more than two pointers are down.
-        if (event.getPointerCount() > 2) {
-            return;
+    public boolean onDoubleTap(MotionEvent event, int policyFlags) {
+        // Ignore the event if we aren't touch exploring.
+        if (mCurrentState != STATE_TOUCH_EXPLORING) {
+            return false;
         }
 
         // Remove pending event deliveries.
@@ -438,7 +416,9 @@
         Point clickLocation = mTempPoint;
         final int result = computeClickLocation(clickLocation);
         if (result == CLICK_LOCATION_NONE) {
-            return;
+            // We can't send a click to no location, but the gesture was still
+            // consumed.
+            return true;
         }
 
         // Do the click.
@@ -456,6 +436,28 @@
         final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS);
         sendActionDownAndUp(click_event, policyFlags, targetAccessibilityFocus);
         click_event.recycle();
+        return true;
+    }
+
+    @Override
+    public boolean onGesture(int gestureId) {
+        if (mCurrentState != STATE_GESTURE_DETECTING) {
+            return false;
+        }
+
+        mAms.onTouchInteractionEnd();
+
+        // Announce the end of the gesture recognition.
+        sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
+        // Announce the end of a the touch interaction.
+        sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+
+        mAms.onGesture(gestureId);
+
+        mExitGestureDetectionModeDelayed.cancel();
+        mCurrentState = STATE_TOUCH_EXPLORING;
+
+        return true;
     }
 
     /**
@@ -471,17 +473,10 @@
 
         mVelocityTracker.addMovement(rawEvent);
 
-        mGestureDetector.onMotionEvent(event, policyFlags);
-
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN: {
                 mAms.onTouchInteractionStart();
 
-                // Pre-feed the motion events to the gesture detector since we
-                // have a distance slop before getting into gesture detection
-                // mode and not using the points within this slop significantly
-                // decreases the quality of gesture recognition.
-                handleMotionEventGestureDetecting(rawEvent, policyFlags);
                 sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
 
                 // If we still have not notified the user for the last
@@ -528,12 +523,6 @@
                         // We have not started sending events since we try to
                         // figure out what the user is doing.
                         if (mSendHoverEnterAndMoveDelayed.isPending()) {
-                            // Pre-feed the motion events to the gesture detector since we
-                            // have a distance slop before getting into gesture detection
-                            // mode and not using the points within this slop significantly
-                            // decreases the quality of gesture recognition.
-                            handleMotionEventGestureDetecting(rawEvent, policyFlags);
-
                             // Cache the event until we discern exploration from gesturing.
                             mSendHoverEnterAndMoveDelayed.addEvent(event);
 
@@ -568,6 +557,7 @@
                                 } else {
                                     // We have just decided that the user is touch,
                                     // exploring so start sending events.
+                                    mGestureDetector.cancelGesture();
                                     mSendHoverEnterAndMoveDelayed.forceSendAndRemove();
                                     mSendHoverExitDelayed.cancel();
                                     sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE,
@@ -613,9 +603,9 @@
                         }
 
                         // We know that a new state transition is to happen and the
-                        // new state will not be gesture recognition, so clear the
-                        // stashed gesture strokes.
-                        mStrokeBuffer.clear();
+                        // new state will not be gesture recognition, so cancel
+                        // the gesture.
+                        mGestureDetector.cancelGesture();
 
                         if (isDraggingGesture(event)) {
                             // Two pointers moving in the same direction within
@@ -655,9 +645,6 @@
             } break;
             case MotionEvent.ACTION_UP: {
                 mAms.onTouchInteractionEnd();
-                // We know that we do not need the pre-fed gesture points are not
-                // needed anymore since the last pointer just went up.
-                mStrokeBuffer.clear();
                 final int pointerId = event.getPointerId(event.getActionIndex());
                 final int pointerIdBits = (1 << pointerId);
 
@@ -820,24 +807,6 @@
 
     private void handleMotionEventGestureDetecting(MotionEvent event, int policyFlags) {
         switch (event.getActionMasked()) {
-            case MotionEvent.ACTION_DOWN: {
-                final float x = event.getX();
-                final float y = event.getY();
-                mPreviousX = x;
-                mPreviousY = y;
-                mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
-            } break;
-            case MotionEvent.ACTION_MOVE: {
-                final float x = event.getX();
-                final float y = event.getY();
-                final float dX = Math.abs(x - mPreviousX);
-                final float dY = Math.abs(y - mPreviousY);
-                if (dX >= TOUCH_TOLERANCE || dY >= TOUCH_TOLERANCE) {
-                    mPreviousX = x;
-                    mPreviousY = y;
-                    mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
-                }
-            } break;
             case MotionEvent.ACTION_UP: {
                 mAms.onTouchInteractionEnd();
                 // Announce the end of the gesture recognition.
@@ -845,31 +814,6 @@
                 // Announce the end of a the touch interaction.
                 sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
 
-                float x = event.getX();
-                float y = event.getY();
-                mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
-
-                Gesture gesture = new Gesture();
-                gesture.addStroke(new GestureStroke(mStrokeBuffer));
-
-                ArrayList<Prediction> predictions = mGestureLibrary.recognize(gesture);
-                if (!predictions.isEmpty()) {
-                    Prediction bestPrediction = predictions.get(0);
-                    if (bestPrediction.score >= MIN_PREDICTION_SCORE) {
-                        if (DEBUG) {
-                            Slog.i(LOG_TAG, "gesture: " + bestPrediction.name + " score: "
-                                    + bestPrediction.score);
-                        }
-                        try {
-                            final int gestureId = Integer.parseInt(bestPrediction.name);
-                            mAms.onGesture(gestureId);
-                        } catch (NumberFormatException nfe) {
-                            Slog.w(LOG_TAG, "Non numeric gesture id:" + bestPrediction.name);
-                        }
-                    }
-                }
-
-                mStrokeBuffer.clear();
                 mExitGestureDetectionModeDelayed.cancel();
                 mCurrentState = STATE_TOUCH_EXPLORING;
             } break;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d031165..c712a56 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1450,10 +1450,7 @@
     }
 
     private void enforceChangePermission() {
-        int uid = Binder.getCallingUid();
-        Settings.checkAndNoteChangeNetworkStateOperation(mContext, uid, Settings
-                .getPackageNameForUid(mContext, uid), true);
-
+        ConnectivityManager.enforceChangePermission(mContext);
     }
 
     private void enforceTetherAccessPermission() {
@@ -3366,7 +3363,7 @@
                     .setPriority(highPriority ?
                             Notification.PRIORITY_HIGH :
                             Notification.PRIORITY_DEFAULT)
-                    .setDefaults(Notification.DEFAULT_ALL)
+                    .setDefaults(highPriority ? Notification.DEFAULT_ALL : 0)
                     .setOnlyAlertOnce(true)
                     .build();
 
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 4d32599..85187c7 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2442,8 +2442,13 @@
         }
 
         try {
-            mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
-                               new SensitiveArg(password));
+            if (type == StorageManager.CRYPT_TYPE_DEFAULT) {
+                mCryptConnector.execute("cryptfs", "enablecrypto", "inplace",
+                                CRYPTO_TYPES[type]);
+            } else {
+                mCryptConnector.execute("cryptfs", "enablecrypto", "inplace",
+                                CRYPTO_TYPES[type], new SensitiveArg(password));
+            }
         } catch (NativeDaemonConnectorException e) {
             // Encryption failed
             return e.getCode();
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4949138..465118c2 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1314,6 +1314,15 @@
         if (!mRestartingServices.contains(r)) {
             return;
         }
+        if (!isServiceNeeded(r, false, false)) {
+            // Paranoia: is this service actually needed?  In theory a service that is not
+            // needed should never remain on the restart list.  In practice...  well, there
+            // have been bugs where this happens, and bad things happen because the process
+            // ends up just being cached, so quickly killed, then restarted again and again.
+            // Let's not let that happen.
+            Slog.wtf(TAG, "Restarting service that is not needed: " + r);
+            return;
+        }
         try {
             bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true);
         } catch (TransactionTooLargeException e) {
@@ -2043,6 +2052,13 @@
                             mAm.mProcessStats);
                     realStartServiceLocked(sr, proc, sr.createdFromFg);
                     didSomething = true;
+                    if (!isServiceNeeded(sr, false, false)) {
+                        // We were waiting for this service to start, but it is actually no
+                        // longer needed.  This could happen because bringDownServiceIfNeeded
+                        // won't bring down a service that is pending...  so now the pending
+                        // is done, so let's drop it.
+                        bringDownServiceLocked(sr);
+                    }
                 }
             } catch (RemoteException e) {
                 Slog.w(TAG, "Exception in new application when starting service "
@@ -2055,7 +2071,7 @@
         // be weird to bring up the process but arbitrarily not let the services
         // run at this point just because their restart time hasn't come up.
         if (mRestartingServices.size() > 0) {
-            ServiceRecord sr = null;
+            ServiceRecord sr;
             for (int i=0; i<mRestartingServices.size(); i++) {
                 sr = mRestartingServices.get(i);
                 if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7df50d5..bb1b6b8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5794,7 +5794,7 @@
         }
 
         boolean didSomething = killPackageProcessesLocked(packageName, appId, userId,
-                -100, callerWillRestart, true, doit, evenPersistent,
+                ProcessList.INVALID_ADJ, callerWillRestart, true, doit, evenPersistent,
                 packageName == null ? ("stop user " + userId) : ("stop " + packageName));
 
         if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
@@ -6110,7 +6110,7 @@
         EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);
 
         app.makeActive(thread, mProcessStats);
-        app.curAdj = app.setAdj = -100;
+        app.curAdj = app.setAdj = ProcessList.INVALID_ADJ;
         app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT;
         app.forcingToForeground = null;
         updateProcessForegroundLocked(app, false, false);
@@ -18057,6 +18057,7 @@
 
         // Examine all activities if not already foreground.
         if (!foregroundActivities && activitiesSize > 0) {
+            int minLayer = ProcessList.VISIBLE_APP_LAYER_MAX;
             for (int j = 0; j < activitiesSize; j++) {
                 final ActivityRecord r = app.activities.get(j);
                 if (r.app != app) {
@@ -18077,6 +18078,12 @@
                     app.cached = false;
                     app.empty = false;
                     foregroundActivities = true;
+                    if (r.task != null && minLayer > 0) {
+                        final int layer = r.task.mLayerRank;
+                        if (layer >= 0 && minLayer > layer) {
+                            minLayer = layer;
+                        }
+                    }
                     break;
                 } else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) {
                     if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
@@ -18117,6 +18124,9 @@
                     }
                 }
             }
+            if (adj == ProcessList.VISIBLE_APP_ADJ) {
+                adj += minLayer;
+            }
         }
 
         if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
@@ -18326,11 +18336,11 @@
                                         && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
                                         && adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
                                     adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                                } else if (clientAdj > ProcessList.VISIBLE_APP_ADJ) {
+                                } else if (clientAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
                                     adj = clientAdj;
                                 } else {
                                     if (adj > ProcessList.VISIBLE_APP_ADJ) {
-                                        adj = ProcessList.VISIBLE_APP_ADJ;
+                                        adj = Math.max(clientAdj, ProcessList.VISIBLE_APP_ADJ);
                                     }
                                 }
                                 if (!client.cached) {
@@ -19368,6 +19378,8 @@
             uidRec.reset();
         }
 
+        mStackSupervisor.rankTaskLayersIfNeeded();
+
         mAdjSeq++;
         mNewNumServiceProcs = 0;
         mNewNumAServiceProcs = 0;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index a59f7ef..1721470 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -993,10 +993,13 @@
                         r.userId, System.identityHashCode(r), r.shortComponentName,
                         mPausingActivity != null
                             ? mPausingActivity.shortComponentName : "(none)");
-                if (r.finishing && r.state == ActivityState.PAUSING) {
-                    if (DEBUG_PAUSE) Slog.v(TAG,
-                            "Executing finish of failed to pause activity: " + r);
-                    finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false);
+                if (r.state == ActivityState.PAUSING) {
+                    r.state = ActivityState.PAUSED;
+                    if (r.finishing) {
+                        if (DEBUG_PAUSE) Slog.v(TAG,
+                                "Executing finish of failed to pause activity: " + r);
+                        finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false);
+                    }
                 }
             }
         }
@@ -1375,6 +1378,20 @@
         return true;
     }
 
+    final int rankTaskLayers(int baseLayer) {
+        int layer = 0;
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            ActivityRecord r = task.topRunningActivityLocked();
+            if (r == null || r.finishing || !r.visible) {
+                task.mLayerRank = -1;
+            } else {
+                task.mLayerRank = baseLayer + layer++;
+            }
+        }
+        return layer;
+    }
+
     /**
      * Make sure that all activities that need to be visible (that is, they
      * currently can be seen by the user) actually are.
@@ -3781,6 +3798,7 @@
                 task.mLastTimeMoved *= -1;
             }
         }
+        mStackSupervisor.invalidateTaskLayers();
     }
 
     void moveHomeStackTaskToTop(int homeStackTaskType) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index d6ad547..54ac58a 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -357,6 +357,9 @@
     // It will be calculated when the default display gets added.
     private int mDefaultMinimalSizeOfResizeableTask = -1;
 
+    // Whether tasks have moved and we need to rank the tasks before next OOM scoring
+    private boolean mTaskLayersChanged = true;
+
     /**
      * Description of a request to start a new activity, which has been held
      * due to app switches being disabled.
@@ -2539,8 +2542,13 @@
     final void doPendingActivityLaunchesLocked(boolean doResume) {
         while (!mPendingActivityLaunches.isEmpty()) {
             PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
-            startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
-                    doResume && mPendingActivityLaunches.isEmpty(), null, null);
+
+            try {
+                startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
+                                             doResume && mPendingActivityLaunches.isEmpty(), null, null);
+            } catch (Exception e) {
+                Slog.w(TAG, "Exception during pending activity launch pal=" + pal, e);
+            }
         }
     }
 
@@ -3608,6 +3616,24 @@
         }
     }
 
+    void invalidateTaskLayers() {
+        mTaskLayersChanged = true;
+    }
+
+    void rankTaskLayersIfNeeded() {
+        if (!mTaskLayersChanged) {
+            return;
+        }
+        mTaskLayersChanged = false;
+        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
+            final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+            int baseLayer = 0;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                baseLayer += stacks.get(stackNdx).rankTaskLayers(baseLayer);
+            }
+        }
+    }
+
     void clearOtherAppTimeTrackers(AppTimeTracker except) {
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 960cbf1..6de8579 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -44,6 +44,7 @@
 import android.os.UserHandle;
 import android.util.EventLog;
 import android.util.Slog;
+import android.util.TimeUtils;
 import com.android.server.DeviceIdleController;
 
 import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -1284,6 +1285,7 @@
 
     final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll, String dumpPackage, boolean needSep) {
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0
                 || mPendingBroadcast != null) {
             boolean printed = false;
@@ -1301,7 +1303,7 @@
                     pw.println("  Active broadcasts [" + mQueueName + "]:");
                 }
                 pw.println("  Active Broadcast " + mQueueName + " #" + i + ":");
-                br.dump(pw, "    ");
+                br.dump(pw, "    ", sdf);
             }
             printed = false;
             needSep = true;
@@ -1319,7 +1321,7 @@
                     pw.println("  Active ordered broadcasts [" + mQueueName + "]:");
                 }
                 pw.println("  Active Ordered Broadcast " + mQueueName + " #" + i + ":");
-                mOrderedBroadcasts.get(i).dump(pw, "    ");
+                mOrderedBroadcasts.get(i).dump(pw, "    ", sdf);
             }
             if (dumpPackage == null || (mPendingBroadcast != null
                     && dumpPackage.equals(mPendingBroadcast.callerPackage))) {
@@ -1328,7 +1330,7 @@
                 }
                 pw.println("  Pending broadcast [" + mQueueName + "]:");
                 if (mPendingBroadcast != null) {
-                    mPendingBroadcast.dump(pw, "    ");
+                    mPendingBroadcast.dump(pw, "    ", sdf);
                 } else {
                     pw.println("    (null)");
                 }
@@ -1366,7 +1368,7 @@
             if (dumpAll) {
                 pw.print("  Historical Broadcast " + mQueueName + " #");
                         pw.print(i); pw.println(":");
-                r.dump(pw, "    ");
+                r.dump(pw, "    ", sdf);
             } else {
                 pw.print("  #"); pw.print(i); pw.print(": "); pw.println(r);
                 pw.print("    ");
@@ -1400,7 +1402,6 @@
             }
             // done skipping; dump the remainder of the ring. 'i' is still the ordinal within
             // the overall broadcast history.
-            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
             do {
                 ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
                 Intent intent = mBroadcastSummaryHistory[ringIndex];
@@ -1422,9 +1423,19 @@
                 i++;
                 pw.print("  #"); pw.print(i); pw.print(": ");
                 pw.println(intent.toShortString(false, true, true, false));
-                pw.print("    enq="); pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex])));
-                pw.print(" disp="); pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex])));
-                pw.print(" fin="); pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex])));
+                pw.print("    ");
+                TimeUtils.formatDuration(mSummaryHistoryDispatchTime[ringIndex]
+                        - mSummaryHistoryEnqueueTime[ringIndex], pw);
+                pw.print(" dispatch ");
+                TimeUtils.formatDuration(mSummaryHistoryFinishTime[ringIndex]
+                        - mSummaryHistoryDispatchTime[ringIndex], pw);
+                pw.println(" finish");
+                pw.print("    enq=");
+                pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex])));
+                pw.print(" disp=");
+                pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex])));
+                pw.print(" fin=");
+                pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex])));
                 Bundle bundle = intent.getExtras();
                 if (bundle != null) {
                     pw.print("    extras: "); pw.println(bundle.toString());
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 1fbfd9f..b42bcff 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -32,6 +32,7 @@
 import android.util.TimeUtils;
 
 import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
@@ -88,7 +89,7 @@
     ComponentName curComponent; // the receiver class that is currently running.
     ActivityInfo curReceiver;   // info about the receiver that is currently running.
 
-    void dump(PrintWriter pw, String prefix) {
+    void dump(PrintWriter pw, String prefix, SimpleDateFormat sdf) {
         final long now = SystemClock.uptimeMillis();
 
         pw.print(prefix); pw.print(this); pw.print(" to user "); pw.println(userId);
@@ -114,13 +115,19 @@
             pw.print(prefix); pw.print("options="); pw.println(options.toBundle());
         }
         pw.print(prefix); pw.print("enqueueClockTime=");
-                pw.print(new Date(enqueueClockTime));
+                pw.print(sdf.format(new Date(enqueueClockTime)));
                 pw.print(" dispatchClockTime=");
-                pw.println(new Date(dispatchClockTime));
+                pw.println(sdf.format(new Date(dispatchClockTime)));
         pw.print(prefix); pw.print("dispatchTime=");
                 TimeUtils.formatDuration(dispatchTime, now, pw);
+                pw.print(" (");
+                TimeUtils.formatDuration(dispatchClockTime-enqueueClockTime, pw);
+                pw.print(" since enq)");
         if (finishTime != 0) {
             pw.print(" finishTime="); TimeUtils.formatDuration(finishTime, now, pw);
+            pw.print(" (");
+            TimeUtils.formatDuration(finishTime-dispatchTime, pw);
+            pw.print(" since disp)");
         } else {
             pw.print(" receiverTime="); TimeUtils.formatDuration(receiverTime, now, pw);
         }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 0e24952..b49370b 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -49,19 +49,22 @@
 
     // OOM adjustments for processes in various states:
 
+    // Uninitialized value for any major or minor adj fields
+    static final int INVALID_ADJ = -10000;
+
     // Adjustment used in certain places where we don't know it yet.
     // (Generally this is something that is going to be cached, but we
     // don't know the exact value in the cached range to assign yet.)
-    static final int UNKNOWN_ADJ = 16;
+    static final int UNKNOWN_ADJ = 1001;
 
     // This is a process only hosting activities that are not visible,
     // so it can be killed without any disruption.
-    static final int CACHED_APP_MAX_ADJ = 15;
-    static final int CACHED_APP_MIN_ADJ = 9;
+    static final int CACHED_APP_MAX_ADJ = 906;
+    static final int CACHED_APP_MIN_ADJ = 900;
 
     // The B list of SERVICE_ADJ -- these are the old and decrepit
     // services that aren't as shiny and interesting as the ones in the A list.
-    static final int SERVICE_B_ADJ = 8;
+    static final int SERVICE_B_ADJ = 800;
 
     // This is the process of the previous application that the user was in.
     // This process is kept above other things, because it is very common to
@@ -69,34 +72,35 @@
     // task switch (toggling between the two top recent apps) as well as normal
     // UI flow such as clicking on a URI in the e-mail app to view in the browser,
     // and then pressing back to return to e-mail.
-    static final int PREVIOUS_APP_ADJ = 7;
+    static final int PREVIOUS_APP_ADJ = 700;
 
     // This is a process holding the home application -- we want to try
     // avoiding killing it, even if it would normally be in the background,
     // because the user interacts with it so much.
-    static final int HOME_APP_ADJ = 6;
+    static final int HOME_APP_ADJ = 600;
 
     // This is a process holding an application service -- killing it will not
     // have much of an impact as far as the user is concerned.
-    static final int SERVICE_ADJ = 5;
+    static final int SERVICE_ADJ = 500;
 
     // This is a process with a heavy-weight application.  It is in the
     // background, but we want to try to avoid killing it.  Value set in
     // system/rootdir/init.rc on startup.
-    static final int HEAVY_WEIGHT_APP_ADJ = 4;
+    static final int HEAVY_WEIGHT_APP_ADJ = 400;
 
     // This is a process currently hosting a backup operation.  Killing it
     // is not entirely fatal but is generally a bad idea.
-    static final int BACKUP_APP_ADJ = 3;
+    static final int BACKUP_APP_ADJ = 300;
 
     // This is a process only hosting components that are perceptible to the
     // user, and we really want to avoid killing them, but they are not
     // immediately visible. An example is background music playback.
-    static final int PERCEPTIBLE_APP_ADJ = 2;
+    static final int PERCEPTIBLE_APP_ADJ = 200;
 
     // This is a process only hosting activities that are visible to the
     // user, so we'd prefer they don't disappear.
-    static final int VISIBLE_APP_ADJ = 1;
+    static final int VISIBLE_APP_ADJ = 100;
+    static final int VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;
 
     // This is the process running the current foreground app.  We'd really
     // rather not kill it!
@@ -104,18 +108,18 @@
 
     // This is a process that the system or a persistent process has bound to,
     // and indicated it is important.
-    static final int PERSISTENT_SERVICE_ADJ = -11;
+    static final int PERSISTENT_SERVICE_ADJ = -700;
 
     // This is a system persistent process, such as telephony.  Definitely
     // don't want to kill it, but doing so is not completely fatal.
-    static final int PERSISTENT_PROC_ADJ = -12;
+    static final int PERSISTENT_PROC_ADJ = -800;
 
     // The system process runs at the default adjustment.
-    static final int SYSTEM_ADJ = -16;
+    static final int SYSTEM_ADJ = -900;
 
     // Special code for native processes that are not being managed by the system (so
     // don't have an oom adj assigned by the system).
-    static final int NATIVE_ADJ = -17;
+    static final int NATIVE_ADJ = -1000;
 
     // Memory pages are 4K.
     static final int PAGE_SIZE = 4*1024;
@@ -159,7 +163,7 @@
     // These must be kept in sync with the definitions in lmkd.c
     //
     // LMK_TARGET <minfree> <minkillprio> ... (up to 6 pairs)
-    // LMK_PROCPRIO <pid> <prio>
+    // LMK_PROCPRIO <pid> <uid> <prio>
     // LMK_PROCREMOVE <pid>
     static final byte LMK_TARGET = 0;
     static final byte LMK_PROCPRIO = 1;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index bd31a21..08203c55b 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -171,10 +171,10 @@
     boolean debugging;          // was app launched for debugging?
     boolean waitedForDebugger;  // has process show wait for debugger dialog?
     Dialog waitDialog;          // current wait for debugger dialog
-    
+
     String shortStringName;     // caching of toShortString() result.
     String stringName;          // caching of toString() result.
-    
+
     // These reports are generated & stored when an app gets into an error condition.
     // They will be "null" when all is OK.
     ActivityManager.ProcessErrorStateInfo crashingReport;
@@ -402,7 +402,7 @@
             }
         }
     }
-    
+
     ProcessRecord(BatteryStatsImpl _batteryStats, ApplicationInfo _info,
             String _processName, int _uid) {
         mBatteryStats = _batteryStats;
@@ -413,8 +413,8 @@
         processName = _processName;
         pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.versionCode));
         maxAdj = ProcessList.UNKNOWN_ADJ;
-        curRawAdj = setRawAdj = -100;
-        curAdj = setAdj = -100;
+        curRawAdj = setRawAdj = ProcessList.INVALID_ADJ;
+        curAdj = setAdj = ProcessList.INVALID_ADJ;
         persistent = false;
         removed = false;
         lastStateTime = lastPssTime = nextPssTime = SystemClock.uptimeMillis();
@@ -560,7 +560,7 @@
         toShortString(sb);
         return shortStringName = sb.toString();
     }
-    
+
     void toShortString(StringBuilder sb) {
         sb.append(pid);
         sb.append(':');
@@ -585,7 +585,7 @@
             }
         }
     }
-    
+
     public String toString() {
         if (stringName != null) {
             return stringName;
@@ -695,7 +695,7 @@
             pkgList.put(info.packageName, new ProcessStats.ProcessStateHolder(info.versionCode));
         }
     }
-    
+
     public String[] getPackageList() {
         int size = pkgList.size();
         if (size == 0) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 1999f49..fe87a93 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -221,6 +221,10 @@
     // default minimal size.
     final int mMinimalSize;
 
+    // Ranking (from top) of this task among all visible tasks. (-1 means it's not visible)
+    // This number will be assigned when we evaluate OOM scores for all visible tasks.
+    int mLayerRank = -1;
+
     Configuration mOverrideConfig = Configuration.EMPTY;
 
     TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 5b40375..81ae8ac 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -793,8 +793,17 @@
     }
 
     @Override // Binder call
+    public int isInTabletMode() {
+        if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE,
+                "isInTabletMode()")) {
+            throw new SecurityException("Requires TABLET_MODE permission");
+        }
+        return getSwitchState(-1, InputDevice.SOURCE_ANY, SW_TABLET_MODE);
+    }
+
+    @Override // Binder call
     public void registerTabletModeChangedListener(ITabletModeChangedListener listener) {
-        if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE_LISTENER,
+        if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE,
                 "registerTabletModeChangedListener()")) {
             throw new SecurityException("Requires TABLET_MODE_LISTENER permission");
         }
@@ -1495,7 +1504,7 @@
                     switchMask);
         }
 
-        if ((switchMask & SW_TABLET_MODE) != 0) {
+        if ((switchMask & SW_TABLET_MODE_BIT) != 0) {
             SomeArgs args = SomeArgs.obtain();
             args.argi1 = (int) (whenNanos & 0xFFFFFFFF);
             args.argi2 = (int) (whenNanos >> 32);
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index ccd2b6e..5376043 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -109,7 +109,13 @@
     int mVerb;
     private AtomicBoolean mCancelled = new AtomicBoolean();
 
-    /** All the information maintained about the job currently being executed. */
+    /**
+     * All the information maintained about the job currently being executed.
+     *
+     * Any reads (dereferences) not done from the handler thread must be synchronized on
+     * {@link #mLock}.
+     * Writes can only be done from the handler thread, or {@link #executeRunnableJob(JobStatus)}.
+     */
     private JobStatus mRunningJob;
     /** Binder to the client service. */
     IJobService service;
@@ -194,7 +200,8 @@
      */
     JobStatus getRunningJob() {
         synchronized (mLock) {
-            return mRunningJob;
+            return mRunningJob == null ?
+                    null : new JobStatus(mRunningJob);
         }
     }
 
@@ -255,15 +262,22 @@
      */
     @Override
     public void onServiceConnected(ComponentName name, IBinder service) {
-        if (!name.equals(mRunningJob.getServiceComponent())) {
+        JobStatus runningJob;
+        synchronized (mLock) {
+            // This isn't strictly necessary b/c the JobServiceHandler is running on the main
+            // looper and at this point we can't get any binder callbacks from the client. Better
+            // safe than sorry.
+            runningJob = mRunningJob;
+        }
+        if (runningJob == null || !name.equals(runningJob.getServiceComponent())) {
             mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
             return;
         }
         this.service = IJobService.Stub.asInterface(service);
         final PowerManager pm =
                 (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mRunningJob.getTag());
-        mWakeLock.setWorkSource(new WorkSource(mRunningJob.getUid()));
+        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, runningJob.getTag());
+        mWakeLock.setWorkSource(new WorkSource(runningJob.getUid()));
         mWakeLock.setReferenceCounted(false);
         mWakeLock.acquire();
         mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
@@ -281,13 +295,15 @@
      * @return True if the binder calling is coming from the client we expect.
      */
     private boolean verifyCallingUid() {
-        if (mRunningJob == null || Binder.getCallingUid() != mRunningJob.getUid()) {
-            if (DEBUG) {
-                Slog.d(TAG, "Stale callback received, ignoring.");
+        synchronized (mLock) {
+            if (mRunningJob == null || Binder.getCallingUid() != mRunningJob.getUid()) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Stale callback received, ignoring.");
+                }
+                return false;
             }
-            return false;
+            return true;
         }
-        return true;
     }
 
     /**
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 69c63f3..c02611f 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -82,6 +82,13 @@
         this.numFailures = numFailures;
     }
 
+    /** Copy constructor. */
+    public JobStatus(JobStatus jobStatus) {
+        this(jobStatus.getJob(), jobStatus.getUid(), jobStatus.getNumFailures());
+        this.earliestRunTimeElapsedMillis = jobStatus.getEarliestRunTime();
+        this.latestRunTimeElapsedMillis = jobStatus.getLatestRunTimeElapsed();
+    }
+
     /** Create a newly scheduled job. */
     public JobStatus(JobInfo job, int uId) {
         this(job, uId, 0);
diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java
index 88ef366..a4d5bce 100644
--- a/services/core/java/com/android/server/notification/EventConditionProvider.java
+++ b/services/core/java/com/android/server/notification/EventConditionProvider.java
@@ -176,7 +176,7 @@
         }
         mTrackers.clear();
         for (UserHandle user : UserManager.get(mContext).getUserProfiles()) {
-            final Context context = user.isOwner() ? mContext : getContextForUser(mContext, user);
+            final Context context = user.isSystem() ? mContext : getContextForUser(mContext, user);
             if (context == null) {
                 Slog.w(TAG, "Unable to create context for user " + user.getIdentifier());
                 continue;
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 8176aff..8abc8fc 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -586,6 +586,8 @@
                     grantRuntimePermissionsLPw(wearHomePackage, PHONE_PERMISSIONS, true, userId);
                     grantRuntimePermissionsLPw(wearHomePackage, MICROPHONE_PERMISSIONS, false,
                             userId);
+                    grantRuntimePermissionsLPw(wearHomePackage, LOCATION_PERMISSIONS, false,
+                            userId);
                 }
             }
 
@@ -596,7 +598,10 @@
     private void grantDefaultPermissionsToDefaultSystemDialerAppLPr(
             PackageParser.Package dialerPackage, int userId) {
         if (doesPackageSupportRuntimePermissions(dialerPackage)) {
-            grantRuntimePermissionsLPw(dialerPackage, PHONE_PERMISSIONS, userId);
+            boolean isPhonePermFixed =
+                    mService.hasSystemFeature(PackageManager.FEATURE_WATCH);
+            grantRuntimePermissionsLPw(
+                    dialerPackage, PHONE_PERMISSIONS, isPhonePermFixed, userId);
             grantRuntimePermissionsLPw(dialerPackage, CONTACTS_PERMISSIONS, userId);
             grantRuntimePermissionsLPw(dialerPackage, SMS_PERMISSIONS, userId);
             grantRuntimePermissionsLPw(dialerPackage, MICROPHONE_PERMISSIONS, userId);
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 6c6871f..56c4fb1 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -28,7 +28,6 @@
 import android.util.Slog;
 
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
@@ -166,12 +165,7 @@
                     String oatDir = null;
                     if (dexoptNeeded == DexFile.DEX2OAT_NEEDED) {
                         dexoptType = "dex2oat";
-                        try {
-                            oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
-                        } catch (IOException ioe) {
-                            Slog.w(TAG, "Unable to create oatDir for package: " + pkg.packageName);
-                            return DEX_OPT_FAILED;
-                        }
+                        oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
                     } else if (dexoptNeeded == DexFile.PATCHOAT_NEEDED) {
                         dexoptType = "patchoat";
                     } else if (dexoptNeeded == DexFile.SELF_PATCHOAT_NEEDED) {
@@ -230,8 +224,7 @@
      * cannot be created.
      */
     @Nullable
-    private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet)
-            throws IOException {
+    private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet) {
         if (!pkg.canHaveOatDir()) {
             return null;
         }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 6e32e5c..b0e43a5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -481,6 +481,7 @@
             throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
                     "Failed to resolve stage location", e);
         }
+        final boolean quickInstall = (params.installFlags & PackageManager.INSTALL_QUICK) != 0;
 
         // Verify that stage looks sane with respect to existing application.
         // This currently only ensures packageName, versionCode, and certificate
@@ -488,7 +489,10 @@
         validateInstallLocked();
 
         Preconditions.checkNotNull(mPackageName);
-        Preconditions.checkNotNull(mSignatures);
+        // TODO: fix b/25118622; don't bypass signature check
+        if (!quickInstall) {
+            Preconditions.checkNotNull(mSignatures);
+        }
         Preconditions.checkNotNull(mResolvedBaseFile);
 
         if (!mPermissionsAccepted) {
@@ -598,6 +602,7 @@
      * {@link PackageManagerService}.
      */
     private void validateInstallLocked() throws PackageManagerException {
+        final boolean quickInstall = (params.installFlags & PackageManager.INSTALL_QUICK) != 0;
         mPackageName = null;
         mVersionCode = -1;
         mSignatures = null;
@@ -621,7 +626,9 @@
 
             final ApkLite apk;
             try {
-                apk = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES);
+                // TODO: fix b/25118622; always use PARSE_COLLECT_CERTIFICATES
+                final int parseFlags = quickInstall ? 0 : PackageParser.PARSE_COLLECT_CERTIFICATES;
+                apk = PackageParser.parseApkLite(file, parseFlags);
             } catch (PackageParserException e) {
                 throw PackageManagerException.from(e);
             }
@@ -742,6 +749,7 @@
     }
 
     private void assertApkConsistent(String tag, ApkLite apk) throws PackageManagerException {
+        final boolean quickInstall = (params.installFlags & PackageManager.INSTALL_QUICK) != 0;
         if (!mPackageName.equals(apk.packageName)) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
                     + apk.packageName + " inconsistent with " + mPackageName);
@@ -751,7 +759,8 @@
                     + " version code " + apk.versionCode + " inconsistent with "
                     + mVersionCode);
         }
-        if (!Signature.areExactMatch(mSignatures, apk.signatures)) {
+        // TODO: fix b/25118622; don't bypass signature check
+        if (!quickInstall && !Signature.areExactMatch(mSignatures, apk.signatures)) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                     tag + " signatures are inconsistent");
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f7f38db..496653e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2321,7 +2321,7 @@
                         + mSdkVersion + "; regranting permissions for internal storage");
                 updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
             }
-            updatePermissionsLPw(null, null, updateFlags);
+            updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
             ver.sdkVersion = mSdkVersion;
 
             // If this is the first boot or an update from pre-M, and it is a normal
@@ -7685,8 +7685,8 @@
         // We would never need to extract libs for forward-locked and external packages,
         // since the container service will do it for us. We shouldn't attempt to
         // extract libs from system app when it was not updated.
-        if (pkg.isForwardLocked() || isExternal(pkg) ||
-            (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) ) {
+        if (pkg.isForwardLocked() || pkg.applicationInfo.isExternalAsec() ||
+                (isSystemApp(pkg) && !pkg.isUpdatedSystemApp())) {
             extractLibs = false;
         }
 
@@ -7967,7 +7967,7 @@
         final String codePath = pkg.codePath;
         final File codeFile = new File(codePath);
         final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp();
-        final boolean asecApp = info.isForwardLocked() || isExternal(info);
+        final boolean asecApp = info.isForwardLocked() || info.isExternalAsec();
 
         info.nativeLibraryRootDir = null;
         info.nativeLibraryRootRequiresIsa = false;
@@ -8365,8 +8365,14 @@
     static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
     static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
 
+    private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo,
+            int flags) {
+        final String volumeUuid = (pkgInfo != null) ? getVolumeUuidForPackage(pkgInfo) : null;
+        updatePermissionsLPw(changingPkg, pkgInfo, volumeUuid, flags);
+    }
+
     private void updatePermissionsLPw(String changingPkg,
-            PackageParser.Package pkgInfo, int flags) {
+            PackageParser.Package pkgInfo, String replaceVolumeUuid, int flags) {
         // Make sure there are no dangling permission trees.
         Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
         while (it.hasNext()) {
@@ -8435,14 +8441,21 @@
         if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
             for (PackageParser.Package pkg : mPackages.values()) {
                 if (pkg != pkgInfo) {
-                    grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0,
-                            changingPkg);
+                    // Only replace for packages on requested volume
+                    final String volumeUuid = getVolumeUuidForPackage(pkg);
+                    final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_ALL) != 0)
+                            && Objects.equals(replaceVolumeUuid, volumeUuid);
+                    grantPermissionsLPw(pkg, replace, changingPkg);
                 }
             }
         }
 
         if (pkgInfo != null) {
-            grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0, changingPkg);
+            // Only replace for packages on requested volume
+            final String volumeUuid = getVolumeUuidForPackage(pkgInfo);
+            final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_PKG) != 0)
+                    && Objects.equals(replaceVolumeUuid, volumeUuid);
+            grantPermissionsLPw(pkgInfo, replace, changingPkg);
         }
     }
 
@@ -10087,6 +10100,10 @@
         if (!DEFAULT_VERIFY_ENABLE) {
             return false;
         }
+        // TODO: fix b/25118622; don't bypass verification
+        if (Build.IS_DEBUGGABLE && (installFlags & PackageManager.INSTALL_QUICK) != 0) {
+            return false;
+        }
 
         boolean ensureVerifyAppsEnabled = isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS);
 
@@ -12412,6 +12429,7 @@
         final boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
         final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
                 || (args.volumeUuid != null));
+        final boolean quickInstall = ((installFlags & PackageManager.INSTALL_QUICK) != 0);
         boolean replace = false;
         int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
         if (args.move != null) {
@@ -12427,7 +12445,8 @@
         final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
                 | PackageParser.PARSE_ENFORCE_CODE
                 | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
-                | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
+                | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)
+                | (quickInstall ? PackageParser.PARSE_SKIP_VERIFICATION : 0);
         PackageParser pp = new PackageParser();
         pp.setSeparateProcesses(mSeparateProcesses);
         pp.setDisplayMetrics(mMetrics);
@@ -12457,7 +12476,6 @@
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
         try {
             pp.collectCertificates(pkg, parseFlags);
-            pp.collectManifestDigest(pkg);
         } catch (PackageParserException e) {
             res.setError("Failed collect during installPackageLI", e);
             return;
@@ -12467,6 +12485,16 @@
 
         /* If the installer passed in a manifest digest, compare it now. */
         if (args.manifestDigest != null) {
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectManifestDigest");
+            try {
+                pp.collectManifestDigest(pkg);
+            } catch (PackageParserException e) {
+                res.setError("Failed collect during installPackageLI", e);
+                return;
+            } finally {
+                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+            }
+
             if (DEBUG_INSTALL) {
                 final String parsedManifest = pkg.manifestDigest == null ? "null"
                         : pkg.manifestDigest.toString();
@@ -12644,7 +12672,7 @@
             int result = mPackageDexOptimizer
                     .performDexOpt(pkg, null /* instruction sets */, false /* forceDex */,
                             false /* defer */, false /* inclDependencies */,
-                            true /*bootComplete*/, false /*useJit*/);
+                            true /*bootComplete*/, quickInstall /*useJit*/);
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
                 res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
@@ -12846,6 +12874,18 @@
         return installFlags;
     }
 
+    private String getVolumeUuidForPackage(PackageParser.Package pkg) {
+        if (isExternal(pkg)) {
+            if (TextUtils.isEmpty(pkg.volumeUuid)) {
+                return StorageManager.UUID_PRIMARY_PHYSICAL;
+            } else {
+                return pkg.volumeUuid;
+            }
+        } else {
+            return StorageManager.UUID_PRIVATE_INTERNAL;
+        }
+    }
+
     private VersionInfo getSettingsVersionForPackage(PackageParser.Package pkg) {
         if (isExternal(pkg)) {
             if (TextUtils.isEmpty(pkg.volumeUuid)) {
@@ -13862,7 +13902,7 @@
             if (ps != null) {
                 libDirRoot = ps.legacyNativeLibraryPathString;
             }
-            if (p != null && (isExternal(p) || p.isForwardLocked())) {
+            if (p != null && (p.isForwardLocked() || p.applicationInfo.isExternalAsec())) {
                 final long token = Binder.clearCallingIdentity();
                 try {
                     String secureContainerId = cidFromCodePath(p.applicationInfo.getBaseCodePath());
@@ -15716,7 +15756,7 @@
         if (isMounted) {
             if (DEBUG_SD_INSTALL)
                 Log.i(TAG, "Loading packages");
-            loadMediaPackages(processCids, uidArr);
+            loadMediaPackages(processCids, uidArr, externalStorage);
             startCleaningPackages();
             mInstallerService.onSecureContainersAvailable();
         } else {
@@ -15771,7 +15811,8 @@
      * the cid is added to list of removeCids. We currently don't delete stale
      * containers.
      */
-    private void loadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int[] uidArr) {
+    private void loadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int[] uidArr,
+            boolean externalStorage) {
         ArrayList<String> pkgList = new ArrayList<String>();
         Set<AsecInstallArgs> keys = processCids.keySet();
 
@@ -15844,7 +15885,10 @@
             // cases get permissions that the user didn't initially explicitly
             // allow... it would be nice to have some better way to handle
             // this situation.
-            final VersionInfo ver = mSettings.getExternalVersion();
+            final VersionInfo ver = externalStorage ? mSettings.getExternalVersion()
+                    : mSettings.getInternalVersion();
+            final String volumeUuid = externalStorage ? StorageManager.UUID_PRIMARY_PHYSICAL
+                    : StorageManager.UUID_PRIVATE_INTERNAL;
 
             int updateFlags = UPDATE_PERMISSIONS_ALL;
             if (ver.sdkVersion != mSdkVersion) {
@@ -15852,7 +15896,7 @@
                         + mSdkVersion + "; regranting permissions for external");
                 updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
             }
-            updatePermissionsLPw(null, null, updateFlags);
+            updatePermissionsLPw(null, null, volumeUuid, updateFlags);
 
             // Yay, everything is now upgraded
             ver.forceCurrent();
@@ -15986,7 +16030,7 @@
                         + mSdkVersion + "; regranting permissions for " + vol.fsUuid);
                 updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
             }
-            updatePermissionsLPw(null, null, updateFlags);
+            updatePermissionsLPw(null, null, vol.fsUuid, updateFlags);
 
             // Yay, everything is now upgraded
             ver.forceCurrent();
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 61d2676..d148a4f 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -514,7 +514,18 @@
         ArrayList<String> removeStage = new ArrayList<String>();
         for (Map.Entry<String,SharedUserSetting> entry : mSharedUsers.entrySet()) {
             final SharedUserSetting sus = entry.getValue();
-            if (sus == null || sus.packages.size() == 0) {
+            if (sus == null) {
+                removeStage.add(entry.getKey());
+                continue;
+            }
+            // remove packages that are no longer installed
+            for (Iterator<PackageSetting> iter = sus.packages.iterator(); iter.hasNext();) {
+                PackageSetting ps = iter.next();
+                if (mPackages.get(ps.name) == null) {
+                    iter.remove();
+                }
+            }
+            if (sus.packages.size() == 0) {
                 removeStage.add(entry.getKey());
             }
         }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 80697ed..b36a22e 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -598,8 +598,6 @@
 
     @Override
     public Bundle getUserRestrictions(int userId) {
-        // checkManageUsersPermission("getUserRestrictions");
-
         synchronized (mPackagesLock) {
             Bundle restrictions = mUserRestrictions.get(userId);
             return restrictions != null ? new Bundle(restrictions) : new Bundle();
@@ -1588,7 +1586,7 @@
     public Bundle getApplicationRestrictionsForUser(String packageName, int userId) {
         if (UserHandle.getCallingUserId() != userId
                 || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
-            checkManageUsersPermission("Only system can get restrictions for other users/apps");
+            checkManageUsersPermission("get application restrictions for other users/apps");
         }
         synchronized (mPackagesLock) {
             // Read the restrictions from XML
@@ -1599,10 +1597,7 @@
     @Override
     public void setApplicationRestrictions(String packageName, Bundle restrictions,
             int userId) {
-        if (UserHandle.getCallingUserId() != userId
-                || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
-            checkManageUsersPermission("Only system can set restrictions for other users/apps");
-        }
+        checkManageUsersPermission("set application restrictions");
         synchronized (mPackagesLock) {
             if (restrictions == null || restrictions.isEmpty()) {
                 cleanAppRestrictionsForPackage(packageName, userId);
@@ -1623,7 +1618,7 @@
 
     @Override
     public void removeRestrictions() {
-        checkManageUsersPermission("Only system can remove restrictions");
+        checkManageUsersPermission("remove restrictions");
         final int userHandle = UserHandle.getCallingUserId();
         removeRestrictionsForUser(userHandle, true);
     }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 276d7cd..ecc1f2c 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1283,8 +1283,6 @@
             mDtDy = tmpFloats[Matrix.MSCALE_Y];
             float x = tmpFloats[Matrix.MTRANS_X];
             float y = tmpFloats[Matrix.MTRANS_Y];
-            int w = frame.width();
-            int h = frame.height();
             mWin.mShownPosition.set((int) x, (int) y);
 
             mShownAlpha = mAlpha;
@@ -1381,7 +1379,6 @@
             // avoid premature clipping with the system decor rect.
             clipRect.set((mHasClipRect && !fullscreen) ? mClipRect : w.mSystemDecorRect);
         }
-
         // Expand the clip rect for surface insets.
         final WindowManager.LayoutParams attrs = w.mAttrs;
         clipRect.left -= attrs.surfaceInsets.left;
@@ -1395,17 +1392,14 @@
             // clip rect extends outside the system decor rect.
             clipRect.intersect(mClipRect);
         }
-
         // The clip rect was generated assuming (0,0) as the window origin,
         // so we need to translate to match the actual surface coordinates.
         clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top);
-
         // We don't want to clip to stack bounds windows that are currently doing entrance
         // animation for docked window, otherwise the animating window will be suddenly cut off.
         if (!(mAnimator.mAnimating && w.inDockedWorkspace())) {
             adjustCropToStackBounds(w, clipRect);
         }
-
         if (!clipRect.equals(mLastClipRect)) {
             mLastClipRect.set(clipRect);
             try {
@@ -1437,18 +1431,24 @@
         if (appToken != null && appToken.mCropWindowsToStack && !appToken.mReplacingWindow) {
             TaskStack stack = w.getTask().mStack;
             stack.getBounds(mTmpStackBounds);
-            final int surfaceX = (int) mSurfaceX;
-            final int surfaceY = (int) mSurfaceY;
+            // When we resize we use the big surface approach, which means we can't trust the
+            // window frame bounds anymore. Instead, the window will be placed at 0, 0, but to avoid
+            // hardcoding it, we use surface coordinates.
+            final boolean isResizing = w.isDragResizing();
+            final int frameX = isResizing ? (int) mSurfaceX :
+                    w.mFrame.left + mWin.mXOffset - w.getAttrs().surfaceInsets.left;
+            final int frameY = isResizing ? (int) mSurfaceY :
+                    w.mFrame.top + mWin.mYOffset - w.getAttrs().surfaceInsets.top;
             // We need to do some acrobatics with surface position, because their clip region is
             // relative to the inside of the surface, but the stack bounds aren't.
             clipRect.left = Math.max(0,
-                    Math.max(mTmpStackBounds.left, surfaceX + clipRect.left) - surfaceX);
+                    Math.max(mTmpStackBounds.left, frameX + clipRect.left) - frameX);
             clipRect.top = Math.max(0,
-                    Math.max(mTmpStackBounds.top, surfaceY + clipRect.top) - surfaceY);
+                    Math.max(mTmpStackBounds.top, frameY + clipRect.top) - frameY);
             clipRect.right = Math.max(0,
-                    Math.min(mTmpStackBounds.right, surfaceX + clipRect.right) - surfaceX);
+                    Math.min(mTmpStackBounds.right, frameX + clipRect.right) - frameX);
             clipRect.bottom = Math.max(0,
-                    Math.min(mTmpStackBounds.bottom, surfaceY + clipRect.bottom) - surfaceY);
+                    Math.min(mTmpStackBounds.bottom, frameY + clipRect.bottom) - frameY);
         }
     }
 
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 8cb0a13..be190cb 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -238,7 +238,8 @@
     /* --- PointerControllerPolicyInterface implementation --- */
 
     virtual void loadPointerResources(PointerResources* outResources);
-    virtual void loadAdditionalMouseResources(std::map<int, SpriteIcon>* outResources);
+    virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources);
+    virtual int32_t getDefaultPointerIconId();
 
 private:
     sp<InputManager> mInputManager;
@@ -786,7 +787,7 @@
   sp<PointerController> controller = mLocked.pointerController.promote();
   if (controller != NULL) {
         // Use 0 (the default icon) for ARROW.
-        controller->updatePointerShape((iconId == POINTER_ICON_STYLE_ARROW) ? 0 : iconId);
+        controller->updatePointerShape(iconId);
   }
 }
 
@@ -1040,15 +1041,19 @@
             &outResources->spotAnchor);
 }
 
-void NativeInputManager::loadAdditionalMouseResources(std::map<int, SpriteIcon>* outResources) {
+void NativeInputManager::loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources) {
     JNIEnv* env = jniEnv();
 
     for (int iconId = POINTER_ICON_STYLE_CONTEXT_MENU; iconId <= POINTER_ICON_STYLE_GRABBING;
              ++iconId) {
         loadSystemIconAsSprite(env, mContextObj, iconId, &((*outResources)[iconId]));
     }
+    loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_NULL, &((*outResources)[POINTER_ICON_STYLE_NULL]));
 }
 
+int32_t NativeInputManager::getDefaultPointerIconId() {
+    return POINTER_ICON_STYLE_ARROW;
+}
 
 // ----------------------------------------------------------------------------
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4748c96..8385685 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3494,7 +3494,7 @@
         final UserHandle caller = mInjector.binderGetCallingUserHandle();
         // If there is a profile owner, redirect to that; otherwise query the device owner.
         ComponentName aliasChooser = getProfileOwner(caller.getIdentifier());
-        if (aliasChooser == null && caller.isOwner()) {
+        if (aliasChooser == null && caller.isSystem()) {
             ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked();
             if (deviceOwnerAdmin != null) {
                 aliasChooser = deviceOwnerAdmin.info.getComponent();
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 703564c..5ecd2b5 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -52,6 +52,32 @@
 public final class PhoneAccount implements Parcelable {
 
     /**
+     * {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which determines the
+     * maximum permitted length of a call subject specified via the
+     * {@link TelecomManager#EXTRA_CALL_SUBJECT} extra on an
+     * {@link android.content.Intent#ACTION_CALL} intent.  Ultimately a {@link ConnectionService} is
+     * responsible for enforcing the maximum call subject length when sending the message, however
+     * this extra is provided so that the user interface can proactively limit the length of the
+     * call subject as the user types it.
+     */
+    public static final String EXTRA_CALL_SUBJECT_MAX_LENGTH =
+            "android.telecom.extra.CALL_SUBJECT_MAX_LENGTH";
+
+    /**
+     * {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which determines the
+     * character encoding to be used when determining the length of messages.
+     * The user interface can use this when determining the number of characters the user may type
+     * in a call subject.  If empty-string, the call subject message size limit will be enforced on
+     * a 1:1 basis.  That is, each character will count towards the messages size limit as a single
+     * character.  If a character encoding is specified, the message size limit will be based on the
+     * number of bytes in the message per the specified encoding.  See
+     * {@link #EXTRA_CALL_SUBJECT_MAX_LENGTH} for more information on the call subject maximum
+     * length.
+     */
+    public static final String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING =
+            "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
+
+    /**
      * Flag indicating that this {@code PhoneAccount} can act as a connection manager for
      * other connections. The {@link ConnectionService} associated with this {@code PhoneAccount}
      * will be allowed to manage phone calls including using its own proprietary phone-call
@@ -213,6 +239,7 @@
             mSupportedUriSchemes.addAll(phoneAccount.getSupportedUriSchemes());
             mIcon = phoneAccount.getIcon();
             mIsEnabled = phoneAccount.isEnabled();
+            mExtras = phoneAccount.getExtras();
         }
 
         /**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7cf5f73..8ebef32 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -268,6 +268,13 @@
             = "carrier_allow_turnoff_ims_bool";
 
     /**
+     * Flag specifying whether Generic Bootstrapping Architecture capable SIM is required for IMS.
+     * @hide
+     */
+    public static final String KEY_CARRIER_IMS_GBA_REQUIRED_BOOL
+            = "carrier_ims_gba_required_bool";
+
+    /**
      * Flag specifying whether IMS instant lettering is available for the carrier.  {@code True} if
      * instant lettering is available for the carrier, {@code false} otherwise.
      * @hide
@@ -535,6 +542,7 @@
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
         sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true);
+        sDefaults.putBoolean(KEY_CARRIER_IMS_GBA_REQUIRED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL, true);
         sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING, "");
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d22727d..6b1b6296 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2874,7 +2874,7 @@
     /**
      * Returns all observed cell information from all radios on the
      * device including the primary and neighboring cells. This does
-     * not cause or change the rate of PhoneStateListner#onCellInfoChanged.
+     * not cause or change the rate of PhoneStateListener#onCellInfoChanged.
      *<p>
      * The list can include one or more of {@link android.telephony.CellInfoGsm CellInfoGsm},
      * {@link android.telephony.CellInfoCdma CellInfoCdma},
@@ -2888,6 +2888,9 @@
      * devices this may return null in which case getCellLocation should
      * be called.
      *<p>
+     * This API will return valid data for registered cells on devices with
+     * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY}
+     *<p>
      * @return List of CellInfo or null if info unavailable.
      *
      * <p>Requires Permission: {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}