Merge "A11y: Show title if ticker is not available" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index be9193e..5e4b2d4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -20728,20 +20728,20 @@
     field public static final int VP8Level_Version2 = 4; // 0x4
     field public static final int VP8Level_Version3 = 8; // 0x8
     field public static final int VP8ProfileMain = 1; // 0x1
-    field public static final int VP9Level1 = 0; // 0x0
-    field public static final int VP9Level11 = 1; // 0x1
-    field public static final int VP9Level2 = 2; // 0x2
-    field public static final int VP9Level21 = 4; // 0x4
-    field public static final int VP9Level3 = 8; // 0x8
-    field public static final int VP9Level31 = 16; // 0x10
-    field public static final int VP9Level4 = 32; // 0x20
-    field public static final int VP9Level41 = 64; // 0x40
-    field public static final int VP9Level5 = 128; // 0x80
-    field public static final int VP9Level51 = 256; // 0x100
-    field public static final int VP9Level52 = 512; // 0x200
-    field public static final int VP9Level6 = 1024; // 0x400
-    field public static final int VP9Level61 = 2048; // 0x800
-    field public static final int VP9Level62 = 4096; // 0x1000
+    field public static final int VP9Level1 = 1; // 0x1
+    field public static final int VP9Level11 = 2; // 0x2
+    field public static final int VP9Level2 = 4; // 0x4
+    field public static final int VP9Level21 = 8; // 0x8
+    field public static final int VP9Level3 = 16; // 0x10
+    field public static final int VP9Level31 = 32; // 0x20
+    field public static final int VP9Level4 = 64; // 0x40
+    field public static final int VP9Level41 = 128; // 0x80
+    field public static final int VP9Level5 = 256; // 0x100
+    field public static final int VP9Level51 = 512; // 0x200
+    field public static final int VP9Level52 = 1024; // 0x400
+    field public static final int VP9Level6 = 2048; // 0x800
+    field public static final int VP9Level61 = 4096; // 0x1000
+    field public static final int VP9Level62 = 8192; // 0x2000
     field public static final int VP9Profile0 = 1; // 0x1
     field public static final int VP9Profile1 = 2; // 0x2
     field public static final int VP9Profile2 = 4; // 0x4
diff --git a/api/system-current.txt b/api/system-current.txt
index b787d86..bde3a8c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -22248,20 +22248,20 @@
     field public static final int VP8Level_Version2 = 4; // 0x4
     field public static final int VP8Level_Version3 = 8; // 0x8
     field public static final int VP8ProfileMain = 1; // 0x1
-    field public static final int VP9Level1 = 0; // 0x0
-    field public static final int VP9Level11 = 1; // 0x1
-    field public static final int VP9Level2 = 2; // 0x2
-    field public static final int VP9Level21 = 4; // 0x4
-    field public static final int VP9Level3 = 8; // 0x8
-    field public static final int VP9Level31 = 16; // 0x10
-    field public static final int VP9Level4 = 32; // 0x20
-    field public static final int VP9Level41 = 64; // 0x40
-    field public static final int VP9Level5 = 128; // 0x80
-    field public static final int VP9Level51 = 256; // 0x100
-    field public static final int VP9Level52 = 512; // 0x200
-    field public static final int VP9Level6 = 1024; // 0x400
-    field public static final int VP9Level61 = 2048; // 0x800
-    field public static final int VP9Level62 = 4096; // 0x1000
+    field public static final int VP9Level1 = 1; // 0x1
+    field public static final int VP9Level11 = 2; // 0x2
+    field public static final int VP9Level2 = 4; // 0x4
+    field public static final int VP9Level21 = 8; // 0x8
+    field public static final int VP9Level3 = 16; // 0x10
+    field public static final int VP9Level31 = 32; // 0x20
+    field public static final int VP9Level4 = 64; // 0x40
+    field public static final int VP9Level41 = 128; // 0x80
+    field public static final int VP9Level5 = 256; // 0x100
+    field public static final int VP9Level51 = 512; // 0x200
+    field public static final int VP9Level52 = 1024; // 0x400
+    field public static final int VP9Level6 = 2048; // 0x800
+    field public static final int VP9Level61 = 4096; // 0x1000
+    field public static final int VP9Level62 = 8192; // 0x2000
     field public static final int VP9Profile0 = 1; // 0x1
     field public static final int VP9Profile1 = 2; // 0x2
     field public static final int VP9Profile2 = 4; // 0x4
diff --git a/api/test-current.txt b/api/test-current.txt
index 4a8d5f6..9141506 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -20796,20 +20796,20 @@
     field public static final int VP8Level_Version2 = 4; // 0x4
     field public static final int VP8Level_Version3 = 8; // 0x8
     field public static final int VP8ProfileMain = 1; // 0x1
-    field public static final int VP9Level1 = 0; // 0x0
-    field public static final int VP9Level11 = 1; // 0x1
-    field public static final int VP9Level2 = 2; // 0x2
-    field public static final int VP9Level21 = 4; // 0x4
-    field public static final int VP9Level3 = 8; // 0x8
-    field public static final int VP9Level31 = 16; // 0x10
-    field public static final int VP9Level4 = 32; // 0x20
-    field public static final int VP9Level41 = 64; // 0x40
-    field public static final int VP9Level5 = 128; // 0x80
-    field public static final int VP9Level51 = 256; // 0x100
-    field public static final int VP9Level52 = 512; // 0x200
-    field public static final int VP9Level6 = 1024; // 0x400
-    field public static final int VP9Level61 = 2048; // 0x800
-    field public static final int VP9Level62 = 4096; // 0x1000
+    field public static final int VP9Level1 = 1; // 0x1
+    field public static final int VP9Level11 = 2; // 0x2
+    field public static final int VP9Level2 = 4; // 0x4
+    field public static final int VP9Level21 = 8; // 0x8
+    field public static final int VP9Level3 = 16; // 0x10
+    field public static final int VP9Level31 = 32; // 0x20
+    field public static final int VP9Level4 = 64; // 0x40
+    field public static final int VP9Level41 = 128; // 0x80
+    field public static final int VP9Level5 = 256; // 0x100
+    field public static final int VP9Level51 = 512; // 0x200
+    field public static final int VP9Level52 = 1024; // 0x400
+    field public static final int VP9Level6 = 2048; // 0x800
+    field public static final int VP9Level61 = 4096; // 0x1000
+    field public static final int VP9Level62 = 8192; // 0x2000
     field public static final int VP9Profile0 = 1; // 0x1
     field public static final int VP9Profile1 = 2; // 0x2
     field public static final int VP9Profile2 = 4; // 0x4
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d440017..ff8cf66 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3430,6 +3430,8 @@
     public static final int FLAG_AND_LOCKED = 1 << 1;
     /** {@hide} */
     public static final int FLAG_AND_UNLOCKED = 1 << 2;
+    /** {@hide} */
+    public static final int FLAG_AND_UNLOCKING_OR_UNLOCKED = 1 << 3;
 
     /**
      * Return whether the given user is actively running.  This means that
@@ -3449,26 +3451,6 @@
     }
 
     /** {@hide} */
-    public boolean isUserRunningAndLocked(int userId) {
-        try {
-            return ActivityManagerNative.getDefault().isUserRunning(userId,
-                    ActivityManager.FLAG_AND_LOCKED);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /** {@hide} */
-    public boolean isUserRunningAndUnlocked(int userId) {
-        try {
-            return ActivityManagerNative.getDefault().isUserRunning(userId,
-                    ActivityManager.FLAG_AND_UNLOCKED);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /** {@hide} */
     public boolean isVrModePackageEnabled(ComponentName component) {
         try {
             return ActivityManagerNative.getDefault().isVrModePackageEnabled(component);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f7c0b4c..6d805ed 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4617,16 +4617,7 @@
             // onConfigurationChanged
             int diff = activity.mCurrentConfig.diff(newConfig);
             if (diff != 0) {
-                // If this activity doesn't handle any of the config changes then don't bother
-                // calling onConfigurationChanged as we're going to destroy it.
-                // Except in the case where the configuration changed on the activity manager side,
-                // but wasn't big enough to cause a resource change so the activity wasn't destroyed.
-                // In this case we still want to change the configuration of the activity but not
-                // report it to the app.
-                if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0
-                        || !reportToActivity) {
-                    shouldChangeConfig = true;
-                }
+                shouldChangeConfig = true;
             }
         }
 
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 7d74a12..fa32848 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -72,8 +72,13 @@
     public static final int S_IWOTH = 00002;
     public static final int S_IXOTH = 00001;
 
-    /** Regular expression for safe filenames: no spaces or metacharacters */
-    private static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+");
+    /** Regular expression for safe filenames: no spaces or metacharacters.
+      *
+      * Use a preload holder so that FileUtils can be compile-time initialized.
+      */
+    private static class NoImagePreloadHolder {
+        public static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+");
+    }
 
     private static final File[] EMPTY = new File[0];
 
@@ -243,7 +248,7 @@
         // Note, we check whether it matches what's known to be safe,
         // rather than what's known to be unsafe.  Non-ASCII, control
         // characters, etc. are all unsafe by default.
-        return SAFE_FILENAME_PATTERN.matcher(file.getPath()).matches();
+        return NoImagePreloadHolder.SAFE_FILENAME_PATTERN.matcher(file.getPath()).matches();
     }
 
     /**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index dd53cbb..d55201f 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -36,7 +36,6 @@
 import android.graphics.BitmapFactory;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.os.storage.StorageManager;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.view.WindowManager.LayoutParams;
@@ -984,10 +983,27 @@
 
     /** {@hide} */
     public boolean isUserUnlocked(@UserIdInt int userId) {
-        // TODO: eventually pivot this back to look at ActivityManager state,
-        // but there is race where we can start a non-encryption-aware launcher
-        // before that lifecycle has entered the running unlocked state.
-        return mContext.getSystemService(StorageManager.class).isUserKeyUnlocked(userId);
+        try {
+            return ActivityManagerNative.getDefault().isUserRunning(userId,
+                    ActivityManager.FLAG_AND_UNLOCKED);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    public boolean isUserUnlockingOrUnlocked(UserHandle user) {
+        return isUserUnlockingOrUnlocked(user.getIdentifier());
+    }
+
+    /** {@hide} */
+    public boolean isUserUnlockingOrUnlocked(@UserIdInt int userId) {
+        try {
+            return ActivityManagerNative.getDefault().isUserRunning(userId,
+                    ActivityManager.FLAG_AND_UNLOCKING_OR_UNLOCKED);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
     }
 
     /**
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index f68e227..fbf7b26 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.PackageManager;
+import android.os.Binder;
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
@@ -1062,11 +1063,20 @@
     }
 
     /** {@hide} */
-    public boolean isUserKeyUnlocked(int userId) {
+    public static boolean isUserKeyUnlocked(int userId) {
+        final IMountService mount = IMountService.Stub
+                .asInterface(ServiceManager.getService("mount"));
+        if (mount == null) {
+            Slog.w(TAG, "Early during boot, assuming locked");
+            return false;
+        }
+        final long token = Binder.clearCallingIdentity();
         try {
-            return mMountService.isUserKeyUnlocked(userId);
+            return mount.isUserKeyUnlocked(userId);
         } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+            throw e.rethrowAsRuntimeException();
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
     }
 
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 8a0759f..893eb37 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -824,6 +824,8 @@
                 UserHandle user, ContentValues values) {
             final ContentResolver resolver = context.getContentResolver();
 
+            // Since we're doing this operation on behalf of an app, we only
+            // want to use the actual "unlocked" state.
             final Uri uri = ContentProvider.maybeAddUserId(
                     userManager.isUserUnlocked(user) ? CONTENT_URI : SHADOW_CONTENT_URI,
                     user.getIdentifier());
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b1bf355..d98e217 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6869,14 +6869,6 @@
                "hdmi_control_auto_device_off_enabled";
 
        /**
-        * Whether to use the DHCP client from Lollipop and earlier instead of the newer Android DHCP
-        * client.
-        * (0 = false, 1 = true)
-        * @hide
-        */
-       public static final String LEGACY_DHCP_CLIENT = "legacy_dhcp_client";
-
-       /**
         * Whether TV will switch to MHL port when a mobile device is plugged in.
         * (0 = false, 1 = true)
         * @hide
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 4737e9b..2d3b6ab 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -284,6 +284,13 @@
      * currently set for that origin. The host application should invoke the
      * specified callback with the desired permission state. See
      * {@link GeolocationPermissions} for details.
+     *
+     * <p>Note that for applications targeting Android N and later SDKs
+     * (API level > {@link android.os.Build.VERSION_CODES#M})
+     * this method is only called for requests originating from secure
+     * origins such as https. On non-secure origins geolocation requests
+     * are automatically denied.</p>
+     *
      * @param origin The origin of the web content attempting to use the
      *               Geolocation API.
      * @param callback The callback to use to set the permission state for the
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index ada7731..f54edf1 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -255,6 +255,16 @@
  * is loading.
  * </p>
  *
+ * <h3>HTML5 Geolocation API support</h3>
+ *
+ * <p>For applications targeting Android N and later releases
+ * (API level > {@link android.os.Build.VERSION_CODES#M}) the geolocation api is only supported on
+ * secure origins such as https. For such applications requests to geolocation api on non-secure
+ * origins are automatically denied without invoking the corresponding
+ * {@link WebChromeClient#onGeolocationPermissionsShowPrompt(String, GeolocationPermissions.Callback)}
+ * method.
+ * </p>
+ *
  * <h3>Layout size</h3>
  * <p>
  * It is recommended to set the WebView layout height to a fixed value or to
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index b6200a1..c21f1df 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -38,6 +38,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.widget.RadialTimePickerView.OnValueSelectedListener;
 
 import com.android.internal.R;
 import com.android.internal.widget.NumericTextView;
@@ -48,8 +49,7 @@
 /**
  * A delegate implementing the radial clock-based TimePicker.
  */
-class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate implements
-        RadialTimePickerView.OnValueSelectedListener {
+class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
     /**
      * Delay in milliseconds before valid but potentially incomplete, for
      * example "1" but not "12", keyboard edits are propagated from the
@@ -88,8 +88,8 @@
 
     private boolean mIsEnabled = true;
     private boolean mAllowAutoAdvance;
-    private int mInitialHourOfDay;
-    private int mInitialMinute;
+    private int mCurrentHour;
+    private int mCurrentMinute;
     private boolean mIs24Hour;
     private boolean mIsAmPmAtStart;
 
@@ -189,8 +189,7 @@
 
         mRadialTimePickerView = (RadialTimePickerView) mainView.findViewById(R.id.radial_picker);
         mRadialTimePickerView.applyAttributes(attrs, defStyleAttr, defStyleRes);
-
-        setupListeners();
+        mRadialTimePickerView.setOnValueSelectedListener(mOnValueSelectedListener);
 
         mAllowAutoAdvance = true;
 
@@ -324,28 +323,24 @@
     }
 
     private void initialize(int hourOfDay, int minute, boolean is24HourView, int index) {
-        mInitialHourOfDay = hourOfDay;
-        mInitialMinute = minute;
+        mCurrentHour = hourOfDay;
+        mCurrentMinute = minute;
         mIs24Hour = is24HourView;
         updateUI(index);
     }
 
-    private void setupListeners() {
-        mRadialTimePickerView.setOnValueSelectedListener(this);
-    }
-
     private void updateUI(int index) {
         updateHeaderAmPm();
-        updateHeaderHour(mInitialHourOfDay, false);
+        updateHeaderHour(mCurrentHour, false);
         updateHeaderSeparator();
-        updateHeaderMinute(mInitialMinute, false);
+        updateHeaderMinute(mCurrentMinute, false);
         updateRadialPicker(index);
 
         mDelegator.invalidate();
     }
 
     private void updateRadialPicker(int index) {
-        mRadialTimePickerView.initialize(mInitialHourOfDay, mInitialMinute, mIs24Hour);
+        mRadialTimePickerView.initialize(mCurrentHour, mCurrentMinute, mIs24Hour);
         setCurrentItemShowing(index, false, true);
     }
 
@@ -358,7 +353,7 @@
             final boolean isAmPmAtStart = dateTimePattern.startsWith("a");
             setAmPmAtStart(isAmPmAtStart);
 
-            updateAmPmLabelStates(mInitialHourOfDay < 12 ? AM : PM);
+            updateAmPmLabelStates(mCurrentHour < 12 ? AM : PM);
         }
     }
 
@@ -388,15 +383,25 @@
      */
     @Override
     public void setHour(int hour) {
-        if (mInitialHourOfDay != hour) {
-            mInitialHourOfDay = hour;
-            updateHeaderHour(hour, true);
-            updateHeaderAmPm();
-            mRadialTimePickerView.setCurrentHour(hour);
-            mRadialTimePickerView.setAmOrPm(mInitialHourOfDay < 12 ? AM : PM);
-            mDelegator.invalidate();
-            onTimeChanged();
+        setHourInternal(hour, false, true);
+    }
+
+    private void setHourInternal(int hour, boolean isFromPicker, boolean announce) {
+        if (mCurrentHour == hour) {
+            return;
         }
+
+        mCurrentHour = hour;
+        updateHeaderHour(hour, announce);
+        updateHeaderAmPm();
+
+        if (!isFromPicker) {
+            mRadialTimePickerView.setCurrentHour(hour);
+            mRadialTimePickerView.setAmOrPm(hour < 12 ? AM : PM);
+        }
+
+        mDelegator.invalidate();
+        onTimeChanged();
     }
 
     /**
@@ -421,13 +426,23 @@
      */
     @Override
     public void setMinute(int minute) {
-        if (mInitialMinute != minute) {
-            mInitialMinute = minute;
-            updateHeaderMinute(minute, true);
-            mRadialTimePickerView.setCurrentMinute(minute);
-            mDelegator.invalidate();
-            onTimeChanged();
+        setMinuteInternal(minute, false);
+    }
+
+    private void setMinuteInternal(int minute, boolean isFromPicker) {
+        if (mCurrentMinute == minute) {
+            return;
         }
+
+        mCurrentMinute = minute;
+        updateHeaderMinute(minute, true);
+
+        if (!isFromPicker) {
+            mRadialTimePickerView.setCurrentMinute(minute);
+        }
+
+        mDelegator.invalidate();
+        onTimeChanged();
     }
 
     /**
@@ -448,7 +463,7 @@
     public void setIs24Hour(boolean is24Hour) {
         if (mIs24Hour != is24Hour) {
             mIs24Hour = is24Hour;
-            mInitialHourOfDay = getHour();
+            mCurrentHour = getHour();
 
             updateHourFormat();
             updateUI(mRadialTimePickerView.getCurrentItemShowing());
@@ -563,34 +578,6 @@
     }
 
     /**
-     * Called by the picker for updating the header display.
-     */
-    @Override
-    public void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance) {
-        switch (pickerIndex) {
-            case HOUR_INDEX:
-                if (mAllowAutoAdvance && autoAdvance) {
-                    updateHeaderHour(newValue, false);
-                    setCurrentItemShowing(MINUTE_INDEX, true, false);
-                    mDelegator.announceForAccessibility(newValue + ". " + mSelectMinutes);
-                } else {
-                    updateHeaderHour(newValue, true);
-                }
-                break;
-            case MINUTE_INDEX:
-                updateHeaderMinute(newValue, true);
-                break;
-            case AMPM_INDEX:
-                updateAmPmLabelStates(newValue);
-                break;
-        }
-
-        if (mOnTimeChangedListener != null) {
-            mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute());
-        }
-    }
-
-    /**
      * Converts hour-of-day (0-23) time into a localized hour number.
      * <p>
      * The localized value may be in the range (0-23), (1-24), (0-11), or
@@ -702,11 +689,43 @@
     private void setAmOrPm(int amOrPm) {
         updateAmPmLabelStates(amOrPm);
 
-        if (mRadialTimePickerView.setAmOrPm(amOrPm) && mOnTimeChangedListener != null) {
-            mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute());
+        if (mRadialTimePickerView.setAmOrPm(amOrPm)) {
+            mCurrentHour = getHour();
+
+            if (mOnTimeChangedListener != null) {
+                mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute());
+            }
         }
     }
 
+    /** Listener for RadialTimePickerView interaction. */
+    private final OnValueSelectedListener mOnValueSelectedListener = new OnValueSelectedListener() {
+        @Override
+        public void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance) {
+            switch (pickerIndex) {
+                case HOUR_INDEX:
+                    final boolean isTransition = mAllowAutoAdvance && autoAdvance;
+                    setHourInternal(newValue, true, !isTransition);
+                    if (isTransition) {
+                        setCurrentItemShowing(MINUTE_INDEX, true, false);
+                        mDelegator.announceForAccessibility(newValue + ". " + mSelectMinutes);
+                    }
+                    break;
+                case MINUTE_INDEX:
+                    setMinuteInternal(newValue, true);
+                    break;
+                case AMPM_INDEX:
+                    updateAmPmLabelStates(newValue);
+                    break;
+            }
+
+            if (mOnTimeChangedListener != null) {
+                mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute());
+            }
+        }
+    };
+
+    /** Listener for keyboard interaction. */
     private final OnValueChangedListener mDigitEnteredListener = new OnValueChangedListener() {
         @Override
         public void onValueChanged(NumericTextView view, int value,
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 7bd64e5..c6b6a7fb 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -45,6 +45,7 @@
         sPackageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED);
         sPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
         sPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+        sPackageFilt.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
         sPackageFilt.addDataScheme("package");
         sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED);
         sNonDataFilt.addAction(Intent.ACTION_USER_STOPPED);
@@ -275,6 +276,9 @@
     public void onFinishPackageChanges() {
     }
 
+    public void onPackageDataCleared(String packageName, int uid) {
+    }
+
     public int getChangingUserId() {
         return mChangeUserId;
     }
@@ -365,6 +369,12 @@
                 }
                 onPackageModified(pkg);
             }
+        } else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) {
+            String pkg = getPackageName(intent);
+            int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
+            if (pkg != null) {
+                onPackageDataCleared(pkg, uid);
+            }
         } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
             mDisappearingPackages = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
             mChangeType = PACKAGE_TEMPORARY_CHANGE;
diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index b44baa2..ccdb024 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -24,6 +24,8 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewParent;
 
 import com.android.internal.R;
 import com.android.internal.util.Preconditions;
@@ -165,7 +167,17 @@
         checkToolbarInitialized();
 
         mContentRectOnScreen.set(mContentRect);
-        mContentRectOnScreen.offset(mViewPositionOnScreen[0], mViewPositionOnScreen[1]);
+
+        // Offset the content rect into screen coordinates, taking into account any transformations
+        // that may be applied to the originating view or its ancestors.
+        final ViewParent parent = mOriginatingView.getParent();
+        if (parent instanceof ViewGroup) {
+            ((ViewGroup) parent).getChildVisibleRect(
+                    mOriginatingView, mContentRectOnScreen, null /* offset */);
+            mContentRectOnScreen.offset(mRootViewPositionOnScreen[0], mRootViewPositionOnScreen[1]);
+        } else {
+            mContentRectOnScreen.offset(mViewPositionOnScreen[0], mViewPositionOnScreen[1]);
+        }
 
         if (isContentRectWithinBounds()) {
             mFloatingToolbarVisibilityHelper.setOutOfBounds(false);
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 1848fe8..bc12391 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -592,9 +592,11 @@
         private void refreshCoordinatesAndOverflowDirection(Rect contentRectOnScreen) {
             refreshViewPort();
 
-            int x = contentRectOnScreen.centerX() - mPopupWindow.getWidth() / 2;
-            // Update x so that the toolbar isn't rendered behind the nav bar in landscape.
-            x = Math.max(0, Math.min(x, mViewPortOnScreen.right - mPopupWindow.getWidth()));
+            // Initialize x ensuring that the toolbar isn't rendered behind the nav bar in
+            // landscape.
+            final int x = Math.min(
+                    contentRectOnScreen.centerX() - mPopupWindow.getWidth() / 2,
+                    mViewPortOnScreen.right - mPopupWindow.getWidth());
 
             final int y;
 
@@ -684,7 +686,8 @@
             int rootViewTopOnWindow = mTmpCoords[1];
             int windowLeftOnScreen = rootViewLeftOnScreen - rootViewLeftOnWindow;
             int windowTopOnScreen = rootViewTopOnScreen - rootViewTopOnWindow;
-            mCoordsOnWindow.set(x - windowLeftOnScreen, y - windowTopOnScreen);
+            mCoordsOnWindow.set(
+                    Math.max(0, x - windowLeftOnScreen), Math.max(0, y - windowTopOnScreen));
         }
 
         /**
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4d86a92..14c17f3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2530,6 +2530,9 @@
   <java-symbol type="string" name="profile_encrypted_message" />
   <java-symbol type="drawable" name="ic_user_secure" />
 
+  <java-symbol type="string" name="android_upgrading_notification_title" />
+  <java-symbol type="string" name="android_upgrading_notification_body" />
+
   <java-symbol type="string" name="usb_mtp_launch_notification_title" />
   <java-symbol type="string" name="usb_mtp_launch_notification_description" />
 
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 8a7d39b..a26850f 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -172,6 +172,10 @@
          access while in power save mode, even if they aren't in the foreground. -->
     <allow-in-power-save-except-idle package="com.android.providers.downloads" />
 
+    <!-- These are the standard packages that are white-listed to always have internet
+         access while in data mode, even if they aren't in the foreground. -->
+    <allow-in-data-usage-save package="com.android.providers.downloads" />
+
     <!-- These are the packages that are white-listed to be able to run as system user -->
     <system-user-whitelisted-app package="com.android.settings" />
 
diff --git a/data/sounds/alarms/material/ogg/Awaken_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Awaken_OG7_1ch_48k.ogg
new file mode 100755
index 0000000..47b1d34
--- /dev/null
+++ b/data/sounds/alarms/material/ogg/Awaken_OG7_1ch_48k.ogg
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg
new file mode 100755
index 0000000..6af4d70
--- /dev/null
+++ b/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Drip_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Drip_OG7_1ch_48k.ogg
new file mode 100755
index 0000000..de3eec2
--- /dev/null
+++ b/data/sounds/alarms/material/ogg/Drip_OG7_1ch_48k.ogg
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Gallop_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Gallop_OG7_1ch_48k.ogg
new file mode 100755
index 0000000..98549b3
--- /dev/null
+++ b/data/sounds/alarms/material/ogg/Gallop_OG7_1ch_48k.ogg
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Nudge_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Nudge_OG7_1ch_48k.ogg
new file mode 100755
index 0000000..7a3d7ed
--- /dev/null
+++ b/data/sounds/alarms/material/ogg/Nudge_OG7_1ch_48k.ogg
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Orbit_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Orbit_OG7_1ch_48k.ogg
new file mode 100755
index 0000000..52e3918
--- /dev/null
+++ b/data/sounds/alarms/material/ogg/Orbit_OG7_1ch_48k.ogg
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Rise_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Rise_OG7_1ch_48k.ogg
new file mode 100755
index 0000000..a2d00cc
--- /dev/null
+++ b/data/sounds/alarms/material/ogg/Rise_OG7_1ch_48k.ogg
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Sway_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Sway_OG7_1ch_48k.ogg
new file mode 100755
index 0000000..273728a
--- /dev/null
+++ b/data/sounds/alarms/material/ogg/Sway_OG7_1ch_48k.ogg
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Wag_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Wag_OG7_1ch_48k.ogg
new file mode 100755
index 0000000..f61d5b9
--- /dev/null
+++ b/data/sounds/alarms/material/ogg/Wag_OG7_1ch_48k.ogg
Binary files differ
diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml
index adb31b8..545923f 100644
--- a/docs/html/_redirects.yaml
+++ b/docs/html/_redirects.yaml
@@ -24,11 +24,11 @@
 - from: /tools/debugging/debugging-projects-cmdline.html
   to: /tools/debugging/index.html
 - from: /sdk/compatibility-library.html
-  to: /tools/support-library/index.html
+  to: /go/libraries/support-library/index.html
 - from: /tools/extras/support-library.html
-  to: /tools/support-library/index.html
+  to: /go/libraries/support-library/index.html
 - from: /training/basics/fragments/support-lib.html
-  to: /tools/support-library/setup.html
+  to: /go/libraries/support-library/setup.html
 - from: /sdk/eclipse-adt.html
   to: /tools/sdk/eclipse-adt.html
 - from: /sdk/RELEASENOTES.html
@@ -72,13 +72,11 @@
 - from: /tools/publishing/app-signing-eclipse.html
   to: /tools/help/adt.html
 - from: /tools/help/uiautomator/.*
-  to: /tools/testing-support-library/index.html
+  to: /go/libraries/testing-support/library/
 - from: /tools/testing/testing_ui.html
   to: /training/testing/ui-testing/index.html
 - from: /tools/testing/activity_test.html
   to: /training/testing/ui-testing/index.html
-- from: /tools/data-binding/index.html
-  to: /tools/data-binding/guide.html
 - from: /tools/testing/what_to_test.html
   to: /distribute/essentials/quality/core.html
 - from: /tools/testing/testing_accessibility.html
@@ -725,3 +723,14 @@
   to: https://commondatastorage.googleapis.com/androiddevelopers/shareables/
 - from: /downloads/
   to: https://commondatastorage.googleapis.com/androiddevelopers/
+
+# Redirects for the new go/libraries/ area
+
+- from: /tools/support-library
+  to: /go/libraries/support-library
+
+- from: /tools/data-binding/...
+  to: /go/libraries/data-binding
+
+- from: /tools/testing-support-library
+  to: /go/libraries/testing-support-library
diff --git a/docs/html/about/_book.yaml b/docs/html/about/_book.yaml
index 04150bd..fdbe53f 100644
--- a/docs/html/about/_book.yaml
+++ b/docs/html/about/_book.yaml
@@ -16,13 +16,19 @@
     path: /about/versions/android-5.1.html
   - title: Android 5.0 APIs
     path: /about/versions/android-5.0.html
-    custom_link_attributes:
-    - es-lang="API de Android 5.0"
-    - ja-lang="Android 5.0 API"
-    - ko-lang="Android 5.0 API"
-    - ru-lang="API для Android 5.0"
-    - zh-cn-lang="Android 5.0 API"
-    - zh-tw-lang="Android 5.0 API"
+    path_attributes:
+    - name: es-lang
+      value: API de Android 5.0
+    - name: ja-lang
+      value: Android 5.0 API
+    - name: ko-lang
+      value: Android 5.0 API
+    - name: ru-lang
+      value: API для Android 5.0
+    - name: zh-cn-lang
+      value: Android 5.0 API
+    - name: zh-tw-lang
+      value: Android 5.0 API
   - title: Android 5.0 Changes
     path: /about/versions/android-5.0-changes.html
 
diff --git a/docs/html/design/_book.yaml b/docs/html/design/_book.yaml
index 4575475..df5406f 100644
--- a/docs/html/design/_book.yaml
+++ b/docs/html/design/_book.yaml
@@ -4,24 +4,35 @@
   section:
   - title: Design Principles
     path: /design/get-started/principles.html
-    custom_link_attributes:
-    - es-lang="Principios de diseño para Android"
-    - ja-lang="Android デザイン指針"
-    - ko-lang="Android 디자인 원칙"
-    - pt-br-lang="Princípios de projeto para Android"
-    - ru-lang="Принципы проектирования Android"
-    - zh-cn-lang="Android 设计原则"
-    - zh-tw-lang="Android 設計原則"
+    path_attributes:
+    - name: es-lang
+      value: Principios de diseño para Android
+    - name: ja-lang
+      value: Android デザイン指針
+    - name: ko-lang
+      value: Android 디자인 원칙
+    - name: pt-br-lang
+      value: Princípios de projeto para Android
+    - name: ru-lang
+      value: Принципы проектирования Android
+    - name: zh-cn-lang
+      value: Android 设计原则
+    - name: zh-tw-lang
+      value: Android 設計原則
   - title: New in Android
     path: /design/patterns/new.html
 
 - title: Material for Android
   path: /design/material/index.html
-  custom_link_attributes:
-  - ja-lang="マテリアル デザイン"
-  - ko-lang="머티어리얼 디자인"
-  - zh-cn-lang="材料设计"
-  - zh-tw-lang="材料設計"
+  path_attributes:
+  - name: ja-lang
+    value: マテリアル デザイン
+  - name: ko-lang
+    value: 머티어리얼 디자인
+  - name: zh-cn-lang
+    value: 材料设计
+  - name: zh-tw-lang
+    value: 材料設計
 
 - title: Devices
   path: /design/devices.html
@@ -64,24 +75,38 @@
     path: /design/style/devices-displays.html
   - title: Navigation
     path: /design/patterns/navigation.html
-    custom_link_attributes:
-    - es-lang="Navegación con los botones Back y Up"
-    - ja-lang="Back と Up を使用したナビゲーション"
-    - ko-lang="뒤로 및 위로 탐색 기능이 포함된 탐색"
-    - pt-br-lang="Navegação com Voltar e Para cima"
-    - ru-lang="Навигация с помощью кнопок \"Назад\" и \"Вверх\""
-    - zh-cn-lang="使用返回和向上导航"
-    - zh-tw-lang="使用 [返回] 及 [上一層] 導覽"
+    path_attributes:
+    - name: es-lang
+      value: Navegación con los botones Back y Up
+    - name: ja-lang
+      value: Back と Up を使用したナビゲーション
+    - name: ko-lang
+      value: 뒤로 및 위로 탐색 기능이 포함된 탐색
+    - name: pt-br-lang
+      value: Navegação com Voltar e Para cima
+    - name: ru-lang
+      value: Навигация с помощью кнопок "Назад" и "Вверх"
+    - name: zh-cn-lang
+      value: 使用返回和向上导航
+    - name: zh-tw-lang
+      value: 使用 [返回] 及 [上一層] 導覽
   - title: Notifications
     path: /design/patterns/notifications.html
-    custom_link_attributes:
-    - es-lang="Notificaciones"
-    - ja-lang="通知"
-    - ko-lang="알림"
-    - pt-br-lang="Notificações"
-    - ru-lang="Уведомления"
-    - zh-cn-lang="通知"
-    - zh-tw-lang="通知"
+    path_attributes:
+    - name: es-lang
+      value: Notificaciones
+    - name: ja-lang
+      value: 通知
+    - name: ko-lang
+      value: 알림
+    - name: pt-br-lang
+      value: Notificações
+    - name: ru-lang
+      value: Уведомления
+    - name: zh-cn-lang
+      value: 通知
+    - name: zh-tw-lang
+      value: 通知
   - title: Widgets
     path: /design/patterns/widgets.html
   - title: Swipe Views
@@ -90,14 +115,21 @@
     path: /design/patterns/fullscreen.html
   - title: Confirming & Acknowledging
     path: /design/patterns/confirming-acknowledging.html
-    custom_link_attributes:
-    - es-lang="Confirmación y reconocimiento"
-    - ja-lang="確認と通知"
-    - ko-lang="확인 및 승인하기"
-    - pt-br-lang="Confirmação e reconhecimento"
-    - ru-lang="Подтверждение и уведомление"
-    - zh-cn-lang="确认和确知"
-    - zh-tw-lang="確認及確認完成"
+    path_attributes:
+    - name: es-lang
+      value: Confirmación y reconocimiento
+    - name: ja-lang
+      value: 確認と通知
+    - name: ko-lang
+      value: 확인 및 승인하기
+    - name: pt-br-lang
+      value: Confirmação e reconhecimento
+    - name: ru-lang
+      value: Подтверждение и уведомление
+    - name: zh-cn-lang
+      value: 确认和确知
+    - name: zh-tw-lang
+      value: 確認及確認完成
   - title: Pure Android
     path: /design/patterns/pure-android.html
   - title: Compatibility
diff --git a/docs/html/distribute/essentials/_book.yaml b/docs/html/distribute/essentials/_book.yaml
index e8b7811..6a2c8f5 100644
--- a/docs/html/distribute/essentials/_book.yaml
+++ b/docs/html/distribute/essentials/_book.yaml
@@ -1,13 +1,15 @@
 toc:
 - title: Core App Quality
   path: /distribute/essentials/quality/core.html
-  custom_link_attributes:
-  - zh-cn-lang="应用的核心质量"
+  path_attributes:
+  - name: zh-cn-lang
+    value: 应用的核心质量
 
 - title: Tablet App Quality
   path: /distribute/essentials/quality/tablets.html
-  custom_link_attributes:
-  - zh-cn-lang="平板电脑应用的质量"
+  path_attributes:
+  - name: zh-cn-lang
+    value: 平板电脑应用的质量
 
 - title: Wear App Quality
   path: /distribute/essentials/quality/wear.html
@@ -20,31 +22,36 @@
 
 - title: Launch Checklist
   path: /distribute/tools/launch-checklist.html
-  custom_link_attributes:
-  - zh-cn-lang="发布检查清单"
+  path_attributes:
+  - name: zh-cn-lang
+    value: 发布检查清单
 
 - title: Localization Checklist
   path: /distribute/tools/localization-checklist.html
-  custom_link_attributes:
-  - zh-cn-lang="本地化检查清单"
+  path_attributes:
+  - name: zh-cn-lang
+    value: 本地化检查清单
 
 - title: Brand Guidelines
   path: /distribute/tools/promote/brand.html
-  custom_link_attributes:
-  - zh-cn-lang="品牌指南"
+  path_attributes:
+  - name: zh-cn-lang
+    value: 品牌指南
 
 - title: Device Art Generator
   path: /distribute/tools/promote/device-art.html
 
 - title: Google Play Badges
   path: https://play.google.com/intl/en_us/badges/
-  custom_link_attributes:
-  - zh-cn-lang="Google Play 徽章生成器"
+  path_attributes:
+  - name: zh-cn-lang
+    value: Google Play 徽章生成器
 
 - title: Linking to Your Products
   path: /distribute/tools/promote/linking.html
-  custom_link_attributes:
-  - zh-cn-lang="链接到您的商品"
+  path_attributes:
+  - name: zh-cn-lang
+    value: 链接到您的商品
 
 - title: Alternative Distribution
   path: /distribute/tools/open-distribution.html
diff --git a/docs/html/distribute/googleplay/_book.yaml b/docs/html/distribute/googleplay/_book.yaml
index 009d1c07..ede18f4 100644
--- a/docs/html/distribute/googleplay/_book.yaml
+++ b/docs/html/distribute/googleplay/_book.yaml
@@ -1,75 +1,119 @@
 toc:
 - title: The Google Play Opportunity
   path: /distribute/googleplay/about.html
-  custom_link_attributes:
-  - es-lang="La oportunidad de Google Play"
-  - ja-lang="Google Play の可能性"
-  - ko-lang="Google Play 활용 기회"
-  - pt-br-lang="A oportunidade do Google Play"
-  - ru-lang="Возможности Google Play"
-  - zh-cn-lang="Google Play 蕴藏的机会"
-  - zh-tw-lang="Google Play商機"
+  path_attributes:
+  - name: es-lang
+    value: La oportunidad de Google Play
+  - name: ja-lang
+    value: Google Play の可能性
+  - name: ko-lang
+    value: Google Play 활용 기회
+  - name: pt-br-lang
+    value: A oportunidade do Google Play
+  - name: ru-lang
+    value: Возможности Google Play
+  - name: zh-cn-lang
+    value: Google Play 蕴藏的机会
+  - name: zh-tw-lang
+    value: Google Play商機
 
 - title: Get Started with Publishing
   path: /distribute/googleplay/start.html
-  custom_link_attributes:
-  - es-lang="Comienza a publicar"
-  - ja-lang="アプリを公開する"
-  - ko-lang="게시 시작하기"
-  - pt-br-lang="Introdução à publicação"
-  - ru-lang="Первые шаги в публикациях"
-  - zh-cn-lang="开始发布"
-  - zh-tw-lang="開始發行"
+  path_attributes:
+  - name: es-lang
+    value: Comienza a publicar
+  - name: ja-lang
+    value: アプリを公開する
+  - name: ko-lang
+    value: 게시 시작하기
+  - name: pt-br-lang
+    value: Introdução à publicação
+  - name: ru-lang
+    value: Первые шаги в публикациях
+  - name: zh-cn-lang
+    value: 开始发布
+  - name: zh-tw-lang
+    value: 開始發行
 
 - title: Developer Console
   path: /distribute/googleplay/developer-console.html
-  custom_link_attributes:
-  - es-lang="Consola para desarrolladores"
-  - ja-lang="デベロッパー コンソール"
-  - ko-lang="개발자 콘솔"
-  - pt-br-lang="Console do Desenvolvedor"
-  - ru-lang="Консоль разработчика"
-  - zh-cn-lang="开发者控制台"
+  path_attributes:
+  - name: es-lang
+    value: Consola para desarrolladores
+  - name: ja-lang
+    value: デベロッパー コンソール
+  - name: ko-lang
+    value: 개발자 콘솔
+  - name: pt-br-lang
+    value: Console do Desenvolvedor
+  - name: ru-lang
+    value: Консоль разработчика
+  - name: zh-cn-lang
+    value: 开发者控制台
 
 - title: Distribute to Android Wear
   path: /distribute/googleplay/wear.html
-  custom_link_attributes:
-  - es-lang="Distribución para Android Wear"
-  - ja-lang="Android Wear への配布"
-  - ko-lang="Android Wear에 배포"
-  - pt-br-lang="Distribuindo para Android Wear"
-  - ru-lang="Распространение приложений Android Wear"
-  - zh-cn-lang="分发到 Android Wear"
-  - zh-tw-lang="散佈至 Android Wear"
+  path_attributes:
+  - name: es-lang
+    value: Distribución para Android Wear
+  - name: ja-lang
+    value: Android Wear への配布
+  - name: ko-lang
+    value: Android Wear에 배포
+  - name: pt-br-lang
+    value: Distribuindo para Android Wear
+  - name: ru-lang
+    value: Распространение приложений Android Wear
+  - name: zh-cn-lang
+    value: 分发到 Android Wear
+  - name: zh-tw-lang
+    value: 散佈至 Android Wear
 
 - title: Distribute to Android TV
   path: /distribute/googleplay/tv.html
-  custom_link_attributes:
-  - es-lang="Distribución para Android TV"
-  - ja-lang="Android TV への配布"
-  - ko-lang="Android TV에 배포"
-  - pt-br-lang="Distribuindo para Android TV"
-  - ru-lang="Распространение приложений в Android TV"
-  - zh-cn-lang="分发到 Android TV"
-  - zh-tw-lang="散佈至 Android 電視"
+  path_attributes:
+  - name: es-lang
+    value: Distribución para Android TV
+  - name: ja-lang
+    value: Android TV への配布
+  - name: ko-lang
+    value: Android TV에 배포
+  - name: pt-br-lang
+    value: Distribuindo para Android TV
+  - name: ru-lang
+    value: Распространение приложений в Android TV
+  - name: zh-cn-lang
+    value: 分发到 Android TV
+  - name: zh-tw-lang
+    value: 散佈至 Android 電視
 
 - title: Distribute to Android Auto
   path: /distribute/googleplay/auto.html
-  custom_link_attributes:
-  - es-lang="Distribución para Android Auto"
-  - ja-lang="Android Auto への配布"
-  - ko-lang="Android Auto에 배포"
-  - pt-br-lang="Distribuindo para o Android Auto"
-  - ru-lang="Распространение приложений для Android Auto"
-  - zh-cn-lang="分发到 Android Auto"
-  - zh-tw-lang="散佈至 Android Auto"
+  path_attributes:
+  - name: es-lang
+    value: Distribución para Android Auto
+  - name: ja-lang
+    value: Android Auto への配布
+  - name: ko-lang
+    value: Android Auto에 배포
+  - name: pt-br-lang
+    value: Distribuindo para o Android Auto
+  - name: ru-lang
+    value: Распространение приложений для Android Auto
+  - name: zh-cn-lang
+    value: 分发到 Android Auto
+  - name: zh-tw-lang
+    value: 散佈至 Android Auto
 
 - title: Designed for Families
   path: /distribute/googleplay/families/about.html
-  custom_link_attributes:
-  - es-lang="Diseñado para la familia"
-  - ru-lang="Для всей семьи"
-  - zh-cn-lang="为家庭设计"
+  path_attributes:
+  - name: es-lang
+    value: Diseñado para la familia
+  - name: ru-lang
+    value: Для всей семьи
+  - name: zh-cn-lang
+    value: 为家庭设计
 
 - title: Google Play for Work
   path: /distribute/googleplay/work/about.html
@@ -90,11 +134,18 @@
 
 - title: Find Success on Google Play
   path: /distribute/googleplay/guide.html
-  custom_link_attributes:
-  - es-lang="Cómo tener éxito en Google Play"
-  - ja-lang="Google Play で成功を手にする"
-  - ko-lang="Google Play에서 성공 모색"
-  - pt-br-lang="Obtendo sucesso no Google Play"
-  - ru-lang="Найдите свой путь к успеху в Google Play"
-  - zh-cn-lang="在 Google Play 上取得成功"
-  - zh-tw-lang="在 Google Play 上尋找成功"
+  path_attributes:
+  - name: es-lang
+    value: Cómo tener éxito en Google Play
+  - name: ja-lang
+    value: Google Play で成功を手にする
+  - name: ko-lang
+    value: Google Play에서 성공 모색
+  - name: pt-br-lang
+    value: Obtendo sucesso no Google Play
+  - name: ru-lang
+    value: Найдите свой путь к успеху в Google Play
+  - name: zh-cn-lang
+    value: 在 Google Play 上取得成功
+  - name: zh-tw-lang
+    value: 在 Google Play 上尋找成功
diff --git a/docs/html/distribute/tools/_book.yaml b/docs/html/distribute/tools/_book.yaml
index 57e03ba..4064c34 100644
--- a/docs/html/distribute/tools/_book.yaml
+++ b/docs/html/distribute/tools/_book.yaml
@@ -1,31 +1,36 @@
 toc:
 - title: Launch Checklist
   path: /distribute/tools/launch-checklist.html
-  custom_link_attributes:
-  - zh-cn-lang="发布检查清单"
+  path_attributes:
+  - name: zh-cn-lang
+    value: 发布检查清单
 
 - title: Localization Checklist
   path: /distribute/tools/localization-checklist.html
-  custom_link_attributes:
-  - zh-cn-lang="本地化检查清单"
+  path_attributes:
+  - name: zh-cn-lang
+    value: 本地化检查清单
 
 - title: Device Art Generator
   path: /distribute/tools/promote/device-art.html
 
 - title: Google Play Badges
   path: /distribute/tools/promote/badges.html
-  custom_link_attributes:
-  - zh-cn-lang="Google Play 徽章生成器"
+  path_attributes:
+  - name: zh-cn-lang
+    value: Google Play 徽章生成器
 
 - title: Linking to Your Products
   path: /distribute/tools/promote/linking.html
-  custom_link_attributes:
-  - zh-cn-lang="链接到您的商品"
+  path_attributes:
+  - name: zh-cn-lang
+    value: 链接到您的商品
 
 - title: Brand Guidelines
   path: /distribute/tools/promote/brand.html
-  custom_link_attributes:
-  - zh-cn-lang="品牌指南"
+  path_attributes:
+  - name: zh-cn-lang
+    value: 品牌指南
 
 - title: Alternative Distribution
   path: /distribute/tools/open-distribution.html
diff --git a/docs/html/go/libraries/_book.yaml b/docs/html/go/libraries/_book.yaml
new file mode 100644
index 0000000..209dae5
--- /dev/null
+++ b/docs/html/go/libraries/_book.yaml
@@ -0,0 +1,17 @@
+toc:
+- title: Support Library
+  path: /tools/support-library/index.html
+  section:
+  - title: Features
+    path: /tools/support-library/features.html
+  - title: Setup
+    path: /tools/support-library/setup.html
+
+- title: Data Binding Library
+  path: /tools/data-binding/guide.html
+
+- title: Testing Support Library
+  path: /tools/testing-support-library/index.html
+  section:
+  - title: API Reference
+    path: /reference/android/support/test/package-summary.html
diff --git a/docs/html/tools/data-binding/guide.jd b/docs/html/go/libraries/data-binding/index.jd
similarity index 99%
rename from docs/html/tools/data-binding/guide.jd
rename to docs/html/go/libraries/data-binding/index.jd
index 8bbd833..ca8784e 100644
--- a/docs/html/tools/data-binding/guide.jd
+++ b/docs/html/go/libraries/data-binding/index.jd
@@ -1,4 +1,5 @@
-page.title=Data Binding Guide
+page.title=Data Binding Library
+page.metaDescription=The Data Binding Library enables you to write declarative layouts.
 page.tags="databinding", "layouts"
 @jd:body
 
diff --git a/docs/html/go/libraries/index.jd b/docs/html/go/libraries/index.jd
new file mode 100644
index 0000000..2831d15
--- /dev/null
+++ b/docs/html/go/libraries/index.jd
@@ -0,0 +1,14 @@
+page.title=Android Libraries
+
+@jd:body
+
+<p>This section describes several useful Android libraries that are not
+included with the Android Framework.</p>
+
+<div class="dynamic-grid">
+  <div class="resource-widget resource-flow-layout landing col-12"
+    data-query="collection:go/libraries"
+    data-cardSizes="6x6"
+    data-maxResults="6">
+  </div>
+</div>
diff --git a/docs/html/tools/support-library/features.jd b/docs/html/go/libraries/support-library/features.jd
similarity index 98%
rename from docs/html/tools/support-library/features.jd
rename to docs/html/go/libraries/support-library/features.jd
index a5f343e..089357a 100755
--- a/docs/html/tools/support-library/features.jd
+++ b/docs/html/go/libraries/support-library/features.jd
@@ -34,11 +34,11 @@
 
     <h2>See also</h2>
     <ol>
-      <li><a href="{@docRoot}tools/support-library/index.html#revisions">
+      <li><a href="{@docRoot}go/libraries/support-library/index.html#revisions">
         Support Library Revisions</a></li>
-      <li><a href="{@docRoot}tools/support-library/setup.html">
+      <li><a href="{@docRoot}go/libraries/support-library/setup.html">
         Support Library Setup</a></li>
-      <li><a href="{@docRoot}tools/testing-support-library/index.html">
+      <li><a href="{@docRoot}go/libraries/testing-support-library/index.html">
         Testing Support Library</a></li>
     </ol>
 
diff --git a/docs/html/tools/support-library/index.jd b/docs/html/go/libraries/support-library/index.jd
similarity index 93%
rename from docs/html/tools/support-library/index.jd
rename to docs/html/go/libraries/support-library/index.jd
index 3afd7ac..61f0bc2 100644
--- a/docs/html/tools/support-library/index.jd
+++ b/docs/html/go/libraries/support-library/index.jd
@@ -1,4 +1,5 @@
 page.title=Support Library
+page.metaDescription=The Android Support Library offers backward-compatible versions of a number of features that are not built into the framework.
 
 @jd:body
 
@@ -8,7 +9,11 @@
     <h2>In this document</h2>
     <ol>
       <li><a href="#overview">Overview</a></li>
-      <li><a href="#revisions">Revisions</a></li>
+      <li><a href="#backward">Backward Compatibility</a></li>
+      <li><a href="#layout-patterns">Support for General Layout Patterns</a></li>
+      <li><a href="#form-factors">Support for Different Form Factors</a></li>
+      <li><a href="#utils">General Utilities</a></li>
+      <li><a href="#revisions">Support Library Revision History</a></li>
     </ol>
 
     <h2>See also</h2>
@@ -22,39 +27,161 @@
   </div>
 </div>
 
-<p>The Android Support Library package is a set of code libraries that provide
-  backward-compatible versions of Android framework APIs as well as features that are only available
-  through the library APIs. Each Support Library is backward-compatible to a specific Android API
-  level. This design means that your applications can use the libraries' features and still be
-  compatible with devices running Android 1.6 (API level 4) and up.</p>
-
-<p>This guide provides information about what features are enabled by the Support Libraries,
-  how to use them in your development environment and information about library releases.</p>
-
+<p>
+  The Android Support Library offers a number of features that are not built
+  into the framework. These libraries offer backward-compatible versions of
+  new features, provide useful UI elements that are not included in the
+  framework, and provide a range of utilities that apps can draw on.
+</p>
 
 <h2 id="overview">Overview</h2>
 
-<p>Including the Support Libraries in your Android project is considered a best practice for
-  application developers, depending on the range of platform versions your app is targeting
-  and the APIs that it uses. Using the features the libraries provide can help you improve the look
-  of your application, increase performance and broaden the reach of your application to more users.
-  If you use the Android
-  <a href="{@docRoot}tools/projects/templates.html">code template</a> tools, you will notice that
-  all the Android application templates include one or more of the Support Libraries by default.</p>
+<p>
+  In many cases, a feature may be valuable to many app developers, but not
+  appropriate to include in the Android framework itself. For example, an app
+  might only need a feature for specialized use cases, such as to smooth the
+  transition between different versions of the Android system.
+</p>
 
-<p>The Support Libraries each target a base Android API level and each provides a different set
-  of features. In order to effectively use the libraries, it is important to consider what features
-  you want to support and understand what features are supported by each library at what Android
-  API level. To get started, review the
-  <a href="{@docRoot}tools/support-library/features.html">Support Library Features</a> guide.
-  After that, go to the
-  <a href="{@docRoot}tools/support-library/setup.html">Support Library Setup</a> topic to
-  learn how to incorporate the Support Libraries into your application. For more details
-  about Support Library APIs, see the {@link android.support.v4.app android.support}
-  packages in the API reference.</p>
+<p>
+  To address these situations, the Android SDK includes several libraries
+  collectively called the <em>Android Support Library</em>. App developers
+  can include any of these libraries if they want to incorporate the
+  library functionality into their apps.
+</p>
+
+<p>
+  Support libraries provide a range of different features:
+</p>
+
+<ul>
+  <li>
+    <a href="#backward">Backward-compatible</a> versions of framework
+    components.
+  </li>
+
+  <li>UI elements to implement the recommended Android <a href=
+  "#layout-patterns">layout patterns</a>.
+  </li>
+
+  <li>Support for different <a href="#form-factors">form factors</a>.
+  </li>
+
+  <li>Miscellaneous <a href="#utils">utility</a> functions.
+  </li>
+</ul>
+
+<h2 id="backward">Backward Compatibility</h2>
+
+<div class="figure" style="width:300px">
+  <img src="{@docRoot}images/tools/support-library/appbar-kitkat.png"
+    srcset="{@docRoot}images/tools/support-library/appbar-kitkat.png 1x,
+        {@docRoot}images/tools/support-library/appbar-kitkat_2x.png 2x"
+    alt="" width="300">
+  <p class="img-caption">
+    <strong>Figure 1.</strong> Because this app uses support library UI
+    elements its interface incorporates material design principles, even though
+    it is running on Android 4.4, which does not include native support for
+    material design.
+  </p>
+</div>
+
+<p>
+  Support libraries allow apps running on older versions of the Android
+  platform to support features made available on newer versions of the
+  platform. For example, an app running on a version of Android lower than 5.0
+  (API level 21) that relies on framework classes cannot display
+  material-design elements, as that version of the Android framework doesn't
+  support material design. However, if the app incorporates the Support
+  Library's <a href="{@docRoot}tools/support-library/features.html">appcompat
+  library</a>, the app has access to many of the features available in API
+  level 21, including support for material design. As a result, your app can
+  deliver a more consistent experience across a broader range of platform
+  versions.
+</p>
 
 
-<h2 id="revisions">Revisions</h2>
+<p>
+  In some cases, the support library version of a class depends as much as
+  possible on the functionality that the framework provides. In these cases,
+  if an app calls one of the support class's methods, the support library's
+  behavior depends on what version of Android the app is running on. If the
+  framework provides the necessary functionality, the support library calls on
+  the framework to perform the task. If the app is running on an earlier
+  version of Android, and the framework doesn't expose the needed
+  functionality, the support library may try to provide the functionality
+  itself, or may act as a no-op. In either case, the app generally doesn't
+  need to check what version of Android it's running on; instead, the app
+  can rely on the support library to do those checks and choose appropriate
+  behavior. Generally, classes whose names end in
+  <code>…Compat</code> (like {@link android.support.v4.app.ActivityCompat})
+  behave this way.
+</p>
+
+<p>
+  In other cases, the support library class provides a complete, standalone
+  version of a framework class that does not rely on the availability of
+  any framework APIs. These
+  methods provide consistent behavior across all supported platforms.
+</p>
+
+<p>
+  In either case, the app does not need to check the system version at run
+  time. The app can rely on the support library class to do the appropriate
+  system checks, and modify its behavior as necessary.
+</p>
+
+<h2 id="layout-patterns">Support for General Layout Patterns</h2>
+
+<p>
+  Support libraries provide user interface elements not offered by
+  the Android framework. For example, the Android Support Library offers additional
+  layout classes, like {@link android.support.v4.widget.DrawerLayout}. These
+  classes follow recommended Android design practices; for example, the Design
+  Library follows the principles of material design in a way that works across
+  many versions of Android.
+</p>
+
+<p>
+  By using these support library classes, you can avoid having to reinvent the
+  wheel; if your app has a particular user interface requirement, you can draw
+  on existing code, which provides a user interface that users will already be
+  familiar with. These elements also help you build an app that looks and feels
+  like a part of the Android ecosystem. For example, many apps need to display
+  arbitrarily long lists of elements, and need to be able to quickly and
+  efficiently reuse those elements as the list changes; this might be a list of
+  emails, contacts, music albums, and so on. Those apps can use the support
+  library {@link android.support.v7.widget.RecyclerView} widget to display the
+  list. This saves the app developer from having to develop the list from
+  scratch, and also ensures that the user will see a list that looks and
+  behaves like lists in other apps.
+</p>
+
+<h2 id="form-factors">Support for Different Form Factors</h2>
+
+<p>
+  The Android SDK provides libraries for a number of different form factors,
+  such as TV and wearables. An app can depend on the appropriate support
+  library to provide functionality across a wide range of platform versions,
+  and can provide content on external screens, speakers, and other destination
+  devices.
+</p>
+
+<h2 id="utils">General Utilities</h2>
+
+<p>
+  The Android Support Library provides backward-compatible utility functions.
+  Apps can use these utility functions to provide an appropriate user
+  experience across a wide range of Android system versions. For example,
+  support library permission methods behave appropriately depending on what
+  platform version your app is running on. If the platform supports the
+  runtime permissions model, these methods request the appropriate permission
+  from the user; on platform versions that do not support the runtime
+  permissions model, the methods check whether the appropriate permission was
+  granted at install time.
+</p>
+
+<h2 id="revisions">Support Library Revision History</h2>
 
 <p>This section provides details about the Support Library package releases.</p>
 
diff --git a/docs/html/tools/support-library/setup.jd b/docs/html/go/libraries/support-library/setup.jd
similarity index 100%
rename from docs/html/tools/support-library/setup.jd
rename to docs/html/go/libraries/support-library/setup.jd
diff --git a/docs/html/tools/testing-support-library/index.jd b/docs/html/go/libraries/testing-support-library/index.jd
similarity index 99%
rename from docs/html/tools/testing-support-library/index.jd
rename to docs/html/go/libraries/testing-support-library/index.jd
index 35a3c7d..f69a35b7 100644
--- a/docs/html/tools/testing-support-library/index.jd
+++ b/docs/html/go/libraries/testing-support-library/index.jd
@@ -1,4 +1,5 @@
 page.title=Testing Support Library
+page.metaDescription=The Android Testing Support Library provides an extensive framework for testing Android apps.
 page.image=images/tools/studio-test-module.png
 
 @jd:body
diff --git a/docs/html/google/_book.yaml b/docs/html/google/_book.yaml
index 01efa4f..92357e9 100644
--- a/docs/html/google/_book.yaml
+++ b/docs/html/google/_book.yaml
@@ -1,17 +1,20 @@
 toc:
 - title: Google Play In-app Billing
   path: /google/play/billing/index.html
-  custom_link_attributes:
-  - zh-cn-lang="应用内结算"
+  path_attributes:
+  - name: zh-cn-lang
+    value: 应用内结算
   section:
   - title: Overview
     path: /google/play/billing/billing_overview.html
-    custom_link_attributes:
-    - zh-cn-lang="应用内结算概述"
+    path_attributes:
+    - name: zh-cn-lang
+      value: 应用内结算概述
   - title: Version 3 API
     path: /google/play/billing/api.html
-    custom_link_attributes:
-    - zh-cn-lang="应用内结算 API"
+    path_attributes:
+    - name: zh-cn-lang
+      value: 应用内结算 API
     section:
     - title: Implementing the API
       path: /google/play/billing/billing_integrate.html
@@ -23,29 +26,39 @@
     path: /google/play/billing/billing_promotions.html
   - title: Security and Design
     path: /google/play/billing/billing_best_practices.html
-    custom_link_attributes:
-    - zh-cn-lang="安全性和设计"
+    path_attributes:
+    - name: zh-cn-lang
+      value: 安全性和设计
   - title: Testing In-app Billing
     path: /google/play/billing/billing_testing.html
-    custom_link_attributes:
-    - zh-cn-lang="测试应用内结算"
+    path_attributes:
+    - name: zh-cn-lang
+      value: 测试应用内结算
   - title: Administering In-app Billing
     path: /google/play/billing/billing_admin.html
-    custom_link_attributes:
-    - zh-cn-lang="管理应用内结算"
+    path_attributes:
+    - name: zh-cn-lang
+      value: 管理应用内结算
   - title: Version Notes
     path: /google/play/billing/versions.html
 
 - title: Filters on Google Play
   path: /google/play/filters.html
-  custom_link_attributes:
-  - es-lang="Filtros en Google Play"
-  - ja-lang="Google Play 上のフィルタ"
-  - ko-lang="Google Play 필터"
-  - pt-br-lang="Filtros no Google Play"
-  - ru-lang="Фильтры в Google Play"
-  - zh-cn-lang="Google Play 上的筛选器"
-  - zh-tw-lang="Google Play 上的篩選器"
+  path_attributes:
+  - name: es-lang
+    value: Filtros en Google Play
+  - name: ja-lang
+    value: Google Play 上のフィルタ
+  - name: ko-lang
+    value: Google Play 필터
+  - name: pt-br-lang
+    value: Filtros no Google Play
+  - name: ru-lang
+    value: Фильтры в Google Play
+  - name: zh-cn-lang
+    value: Google Play 上的筛选器
+  - name: zh-tw-lang
+    value: Google Play 上的篩選器
 
 - title: Google Play Developer API
   path: /google/play/developer-api.html
diff --git a/docs/html/guide/_book.yaml b/docs/html/guide/_book.yaml
index 5017376..4163f0f 100644
--- a/docs/html/guide/_book.yaml
+++ b/docs/html/guide/_book.yaml
@@ -365,6 +365,12 @@
   - title: App Install Location
     path: /guide/topics/data/install-location.html
 
+
+- title: Libraries
+  path: /go/libraries/index.html
+  section:
+  - include: /go/libraries/_book.yaml
+
 - title: Administration
   path: /guide/topics/admin/index.html
   section:
@@ -387,14 +393,21 @@
 
 - title: Best Practices
   path: /guide/practices/index.html
-  custom_link_attributes:
-  - de-lang="Bewährte Verfahren"
-  - es-lang="Prácticas recomendadas"
-  - fr-lang="Meilleures pratiques"
-  - it-lang="Best practice"
-  - ja-lang="ベスト プラクティス"
-  - zh-cn-lang="最佳实践"
-  - zh-tw-lang="最佳實務"
+  path_attributes:
+  - name: de-lang
+    value: Bewährte Verfahren
+  - name: es-lang
+    value: Prácticas recomendadas
+  - name: fr-lang
+    value: Meilleures pratiques
+  - name: it-lang
+    value: Best practice
+  - name: ja-lang
+    value: ベスト プラクティス
+  - name: zh-cn-lang
+    value: 最佳实践
+  - name: zh-tw-lang
+    value: 最佳實務
   section:
   - title: Supporting Multiple Screens
     path: /guide/practices/screens_support.html
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 549a1b6..9257a76 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -121,7 +121,7 @@
       <li><a href="<?cs var:toroot ?>guide/topics/resources/overview.html">
           <span class="en">Overview</span>
         </a></li>
-        <li><a href="<?cs var:toroot ?>guide/topics/resources/providing-resources.html">
+      <li><a href="<?cs var:toroot ?>guide/topics/resources/providing-resources.html">
           <span class="en">Providing Resources</span>
         </a></li>
       <li><a href="<?cs var:toroot ?>guide/topics/resources/accessing-resources.html">
@@ -129,10 +129,13 @@
         </a></li>
       <li><a href="<?cs var:toroot ?>guide/topics/resources/runtime-changes.html">
             <span class="en">Handling Runtime Changes</span>
-          </a></li>
+        </a></li>
       <li><a href="<?cs var:toroot ?>guide/topics/resources/localization.html">
           <span class="en">Localization</span>
         </a></li>
+      <li><a href="<?cs var:toroot ?>guide/topics/resources/complex-xml-resources.html">
+          <span class="en">Complex XML Resources</span>
+        </a></li>
       <li class="nav-section">
         <div class="nav-section-header"><a href="<?cs var:toroot ?>guide/topics/resources/available-resources.html">
             <span class="en">Resource Types</span>
diff --git a/docs/html/guide/topics/resources/complex-xml-resources.jd b/docs/html/guide/topics/resources/complex-xml-resources.jd
new file mode 100644
index 0000000..66dcb58
--- /dev/null
+++ b/docs/html/guide/topics/resources/complex-xml-resources.jd
@@ -0,0 +1,117 @@
+page.title=Inline Complex XML Resources
+parent.title=Application Resources
+parent.link=index.html
+@jd:body
+
+<p>Certain resource types are a composition of multiple complex resources represented by XML files.
+One example is an animated vector drawable, which is a drawable resource encapsulating a vector
+drawable and an animation. This requires the use of at least three XML files.</p>
+
+<dl>
+
+<dt><code>res/drawable/avd.xml</code></dt>
+<dd>
+<pre class="stx">
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/vectordrawable" &gt;
+    &lt;target
+        android:name="rotationGroup"
+        android:animation="@anim/rotation" /&gt;
+&lt;/animated-vector&gt;
+</pre>
+</dd>
+
+<dt><code>res/drawable/vectordrawable.xml</code></dt>
+<dd>
+<pre class="stx">
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="64dp"
+    android:width="64dp"
+    android:viewportHeight="600"
+    android:viewportWidth="600" &gt;
+
   &lt;group
+        android:name="rotationGroup"
+        android:pivotX="300.0"
+        android:pivotY="300.0"
+        android:rotation="45.0" &gt;
+        &lt;path
+            android:fillColor="#000000"
+            android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" /&gt;
+
   &lt;/group&gt;
+&lt;/vector&gt;
+</pre>
+</dd>
+
+<dt><code>res/anim/rotation.xml</code></dt>
+<dd>
+<pre class="stx">
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;objectAnimator xmlns:android="http://schemas.android.com/apk/android"
+    android:duration="6000"
+    android:propertyName="rotation"
+    android:valueFrom="0"
+
   android:valueTo="360" /&gt;
+</pre>
+</dd>
+
+</dl>
+
+<p>There are a lot of files here just to make a single animated vector drawable!
+If the vector drawable and animations are re-used elsewhere, this is the best way to implement an
+animated vector drawable. If they’re only ever used for this animated vector drawable, then there is
+a more compact way to implement them.</p>
+
+<p>Using AAPT’s inline resource format, you can define all three resources in the same XML file.
+Since we’re making an animated vector drawable, we put the file under <code>res/drawable/</code>.</p>
+
+<dl>
+
+<dt><code>res/drawable/avd.xml</code></dt>
+<dd>
+<pre class="stx">
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    <strong>xmlns:aapt="http://schemas.android.com/aapt"</strong> &gt;
+
+    <strong>&lt;aapt:attr name="android:drawable" &gt;</strong>
+        &lt;vector
+            android:height="64dp"
+            android:width="64dp"
+            android:viewportHeight="600"
+            android:viewportWidth="600" &gt;
+
           &lt;group
+                android:name="rotationGroup"
+                android:pivotX="300.0"
+                android:pivotY="300.0"
+                android:rotation="45.0" &gt;
+                &lt;path
+                    android:fillColor="#000000"
+                    android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" /&gt;
+
           &lt;/group&gt;
+        &lt;/vector&gt;
+    <strong>&lt;aapt:attr /&gt;</strong>
+
+    &lt;target
+        android:name="rotationGroup" /&gt;
+        <strong>&lt;aapt:attr name="android:animation" &gt;</strong>
+            &lt;objectAnimator
+                android:duration="6000"
+                android:propertyName="rotation"
+                android:valueFrom="0"
+
               android:valueTo="360" /&gt;
+        <strong>&lt;aapt:attr&gt;</strong>
+&lt;/animated-vector&gt;
+</pre>
+</dd>
+
+</dl>
+
+<p>The XML tag <code>&lt;aapt:attr &gt;</code> tells AAPT that the tag’s child shall be treated as a
+resource and extracted into its own resource file. The value in the attribute name specifies where
+to use the inline resource within the parent tag.</p>
+
+<p>AAPT will generate resource files and names for all of the inline resources.
+Applications built using this inline format are compatible with all versions of Android.</p>
+
diff --git a/docs/html/guide/topics/resources/overview.jd b/docs/html/guide/topics/resources/overview.jd
index c3bd0bf..c496797 100644
--- a/docs/html/guide/topics/resources/overview.jd
+++ b/docs/html/guide/topics/resources/overview.jd
@@ -9,6 +9,7 @@
     <li><a href="accessing-resources.html">Accessing Resources</a></li>
     <li><a href="runtime-changes.html">Handling Runtime Changes</a></li>
     <li><a href="localization.html">Localization</a></li>
+    <li><a href="complex-xml-resources.html">Complex XML Resources</a></li>
   </ol>
 
   <h2>Reference</h2>
@@ -79,6 +80,9 @@
   <dd>A bottom-up guide to localizing your application using alternative resources. While this is
 just one specific use of alternative resources, it is very important in order to reach more
 users.</dd>
+  <dt><strong><a href="complex-xml-resources.html">Complex XML Resources</a></strong></dt>
+  <dd>An XML format for building complex resources like animated vector drawables in a single
+XML file.</dd>
   <dt><strong><a href="available-resources.html">Resource Types</a></strong></dt>
   <dd>A reference of various resource types you can provide, describing their XML elements,
 attributes, and syntax. For example, this reference shows you how to create a resource for
diff --git a/docs/html/images/tools/support-library/appbar-kitkat.png b/docs/html/images/tools/support-library/appbar-kitkat.png
new file mode 100644
index 0000000..c89fd87
--- /dev/null
+++ b/docs/html/images/tools/support-library/appbar-kitkat.png
Binary files differ
diff --git a/docs/html/images/tools/support-library/appbar-kitkat_2x.png b/docs/html/images/tools/support-library/appbar-kitkat_2x.png
new file mode 100644
index 0000000..c9718e6
--- /dev/null
+++ b/docs/html/images/tools/support-library/appbar-kitkat_2x.png
Binary files differ
diff --git a/docs/html/jd_extras_en.js b/docs/html/jd_extras_en.js
index ad66f79..b1d7efc 100644
--- a/docs/html/jd_extras_en.js
+++ b/docs/html/jd_extras_en.js
@@ -4456,6 +4456,14 @@
       "https://support.google.com/googleplay/answer/2651410"
     ]
   },
+  "go/libraries": {
+    "title": "",
+    "resources": [
+      "go/libraries/support-library/index.html",
+      "go/libraries/testing-support-library/index.html",
+      "go/libraries/data-binding/index.html"
+    ]
+  },
   "autolanding": {
     "title": "",
     "resources": [
diff --git a/docs/html/ndk/guides/_book.yaml b/docs/html/ndk/guides/_book.yaml
index fdcfe46..287a92d 100644
--- a/docs/html/ndk/guides/_book.yaml
+++ b/docs/html/ndk/guides/_book.yaml
@@ -61,5 +61,14 @@
   - title: OpenSL ES for Android
     path: /ndk/guides/audio/opensl-for-android.html
 
-- title: Graphics
+- title: Vulkan
   path: /ndk/guides/graphics/index.html
+  section:
+  - title: Getting Started
+    path: /ndk/guides/graphics/getting-started.html
+  - title: Design Guidelines
+    path: /ndk/guides/graphics/design-notes.html
+  - title: Shader Compilers
+    path: /ndk/guides/graphics/shader-compilers.html
+  - title: Validation Layers
+    path: /ndk/guides/graphics/validation-layer.html
diff --git a/docs/html/preview/_book.yaml b/docs/html/preview/_book.yaml
index 9f53866..cac6d13 100644
--- a/docs/html/preview/_book.yaml
+++ b/docs/html/preview/_book.yaml
@@ -1,96 +1,342 @@
 toc:
 - title: Program Overview
   path: /preview/overview.html
-  custom_link_attributes:
-  - es-lang="Información general del programa"
-  - ja-lang="プログラム概要"
-  - ko-lang="프로그램 개요"
-  - pt-br-lang="Visão geral do programa"
-  - ru-lang="Обзор программы"
-  - zh-cn-lang="计划概览"
-  - zh-tw-lang="程式總覽"
+  path_attributes:
+  - name: es-lang
+    value: Información general del programa
+  - name: in-lang
+    value: Ikhtisar Program
+  - name: ja-lang
+    value: プログラム概要
+  - name: ko-lang
+    value: 프로그램 개요
+  - name: pt-br-lang
+    value: Visão geral do programa
+  - name: ru-lang
+    value: Обзор программы
+  - name: vi-lang
+    value: Tổng quan về Chương trình
+  - name: zh-cn-lang
+    value: 计划概览
+  - name: zh-tw-lang
+    value: 程式總覽
 
 - title: Support and Release Notes
   path: /preview/support.html
 
 - title: Set Up to Develop
   path: /preview/setup-sdk.html
-  custom_link_attributes:
-  - es-lang="Configurar el SDK de la versión preliminar"
-  - ja-lang="Preview SDK のセットアップ"
-  - ko-lang="미리 보기 SDK 설정하기"
-  - pt-br-lang="Configuração do Preview SDK"
-  - ru-lang="Настройка пакета SDK Preview"
-  - zh-cn-lang="设置预览版 SDK"
-  - zh-tw-lang="設定預覽版 SDK"
+  path_attributes:
+  - name: es-lang
+    value: Configurar el SDK de la versión preliminar
+  - name: in-lang
+    value: Menyiapkan Preview
+  - name: ja-lang
+    value: Preview SDK のセットアップ
+  - name: ko-lang
+    value: 미리 보기 SDK 설정하기
+  - name: pt-br-lang
+    value: Configuração do Preview SDK
+  - name: ru-lang
+    value: Настройка пакета SDK Preview
+  - name: vi-lang
+    value: Kiểm thử trên Thiết bị
+  - name: zh-cn-lang
+    value: 设置预览版 SDK
+  - name: zh-tw-lang
+    value: 設定預覽版 SDK
 
 - title: Test on a Device
   path: /preview/download.html
+  path_attributes:
+  - name: es-lang
+    value: Pruebe en un dispositivo
+  - name: in-lang
+    value: Menguji pada Perangkat
+  - name: ja-lang
+    value: デバイス上でテストする
+  - name: ko-lang
+    value: 기기에서 테스트
+  - name: pt-br-lang
+    value: Testar em um dispositivo
+  - name: ru-lang
+    value: Тестирование на устройстве
+  - name: vi-lang
+    value: Kiểm thử trên Thiết bị
+  - name: zh-cn-lang
+    value: 在设备上测试
+  - name: zh-tw-lang
+    value: 在裝置上測試
 
 - title: Behavior Changes
   path: /preview/behavior-changes.html
-  custom_link_attributes:
-  - es-lang="Cambios en los comportamientos"
-  - ja-lang="動作の変更点"
-  - ko-lang="동작 변경"
-  - pt-br-lang="Mudanças de comportamento"
-  - ru-lang="Изменения в работе"
-  - zh-cn-lang="行为变更"
-  - zh-tw-lang="行為變更"
+  path_attributes:
+  - name: es-lang
+    value: Cambios en los comportamientos
+  - name: in-lang
+    value: Perubahan Perilaku
+  - name: ja-lang
+    value: 動作の変更点
+  - name: ko-lang
+    value: 동작 변경
+  - name: pt-br-lang
+    value: Mudanças de comportamento
+  - name: ru-lang
+    value: Изменения в работе
+  - name: vi-lang
+    value: Các thay đổi Hành vi
+  - name: zh-cn-lang
+    value: 行为变更
+  - name: zh-tw-lang
+    value: 行為變更
   section:
   - title: Background Optimizations
     path: /preview/features/background-optimization.html
+    path_attributes:
+    - name: es-lang
+      value: Optimizaciones en segundo plano
+    - name: in-lang
+      value: Optimisasi Latar Belakang
+    - name: ja-lang
+      value: バックグラウンド処理の最適化
+    - name: ko-lang
+      value: 백그라운드 최적화
+    - name: pt-br-lang
+      value: Otimizações em segundo plano
+    - name: ru-lang
+      value: Оптимизация фоновых процессов
+    - name: vi-lang
+      value: Tối ưu hóa Chạy ngầm
+    - name: zh-cn-lang
+      value: 后台优化
+    - name: zh-tw-lang
+      value: 背景最佳化
   - title: Language and Locale
     path: /preview/features/multilingual-support.html
+    path_attributes:
+    - name: es-lang
+      value: Idioma y configuración regional
+    - name: in-lang
+      value: Bahasa dan Lokal
+    - name: ja-lang
+      value: 言語とロケール
+    - name: ko-lang
+      value: 언어 및 로케일
+    - name: pt-br-lang
+      value: Idioma e localidade
+    - name: ru-lang
+      value: Язык и языковой стандарт
+    - name: vi-lang
+      value: Ngôn ngữ và Bản địa
+    - name: zh-cn-lang
+      value: 语言和区域设置
+    - name: zh-tw-lang
+      value: 語言和地區設定
 
 - title: Android N for Developers
   path: /preview/api-overview.html
-  custom_link_attributes:
-  - es-lang="Información general de la API"
-  - ja-lang="API の概要"
-  - ko-lang="API 개요"
-  - pt-br-lang="Visão geral da API"
-  - ru-lang="Обзор API-интерфейсов"
-  - zh-cn-lang="API 概览"
-  - zh-tw-lang="API 總覽"
+  path_attributes:
+  - name: es-lang
+    value: Información general de la API
+  - name: in-lang
+    value: Android N untuk Pengembang
+  - name: ja-lang
+    value: API の概要
+  - name: ko-lang
+    value: API 개요
+  - name: pt-br-lang
+    value: Visão geral da API
+  - name: ru-lang
+    value: Обзор API-интерфейсов
+  - name: vi-lang
+    value: Android N cho Nhà phát triển
+  - name: zh-cn-lang
+    value: API 概览
+  - name: zh-tw-lang
+    value: API 總覽
   section:
   - title: Multi-Window Support
     path: /preview/features/multi-window.html
+    path_attributes:
+    - name: es-lang
+      value: Compatibilidad con ventanas múltiples
+    - name: in-lang
+      value: Dukungan Multi-Jendela
+    - name: ja-lang
+      value: マルチ ウィンドウのサポート
+    - name: ko-lang
+      value: 다중 창 지원
+    - name: pt-br-lang
+      value: Suporte a várias janelas
+    - name: ru-lang
+      value: Поддержка многооконного режима
+    - name: vi-lang
+      value: Hỗ trợ đa cửa sổ
+    - name: zh-cn-lang
+      value: 多窗口支持
+    - name: zh-tw-lang
+      value: 多視窗支援
   - title: Notifications
     path: /preview/features/notification-updates.html
+    path_attributes:
+    - name: es-lang
+      value: Notificaciones
+    - name: in-lang
+      value: Pemberitahuan
+    - name: ja-lang
+      value: 通知
+    - name: ko-lang
+      value: 알림
+    - name: pt-br-lang
+      value: Notificações
+    - name: ru-lang
+      value: Уведомления
+    - name: vi-lang
+      value: Thông báo
+    - name: zh-cn-lang
+      value: 通知
+    - name: zh-tw-lang
+      value: 通知
   - title: Data Saver
     path: /preview/features/data-saver.html
   - title: TV Recording
     path: /preview/features/tv-recording-api.html
+    path_attributes:
+    - name: es-lang
+      value: Grabación de TV
+    - name: in-lang
+      value: Perekaman TV
+    - name: ja-lang
+      value: TV の録画
+    - name: ko-lang
+      value: TV 녹화
+    - name: pt-br-lang
+      value: Gravação para TV
+    - name: ru-lang
+      value: Запись ТВ
+    - name: vi-lang
+      value: Ghi lại TV
+    - name: zh-cn-lang
+      value: TV 录制
+    - name: zh-tw-lang
+      value: 電視錄製
   - title: Network Security Configuration
     path: /preview/features/security-config.html
+    path_attributes:
+    - name: es-lang
+      value: Configuración de seguridad de la red
+    - name: ja-lang
+      value: ネットワーク セキュリティ構成
+    - name: ko-lang
+      value: 네트워크 보안 구성
+    - name: pt-br-lang
+      value: Configurações de segurança de rede
+    - name: ru-lang
+      value: Конфигурация сетевой безопасности
+    - name: vi-lang
+      value: Cấu hình Bảo mật mạng
+    - name: zh-cn-lang
+      value: 网络安全配置
+    - name: zh-tw-lang
+      value: 網路安全性設定
   - title: ICU4J Support
     path: /preview/features/icu4j-framework.html
+    path_attributes:
+    - name: es-lang
+      value: API de ICU4J del framework de Android
+    - name: in-lang
+      value: ICU4J Android Framework API
+    - name: ja-lang
+      value: ICU4J Android フレームワーク API
+    - name: ko-lang
+      value: ICU4J Android 프레임워크 API
+    - name: pt-br-lang
+      value: APIs de estrutura do Android para ICU4J
+    - name: ru-lang
+      value: API-интерфейсы ICU4J в платформе Android
+    - name: vi-lang
+      value: API Khuôn khổ Android ICU4J
+    - name: zh-cn-lang
+      value: ICU4J Android 框架 API
+    - name: zh-tw-lang
+      value: ICU4J Android 架構 API
   - title: Java 8 Language Features
     path: /preview/j8-jack.html
+    path_attributes:
+    - name: es-lang
+      value: Funciones del lenguaje Java 8
+    - name: in-lang
+      value: Fitur Bahasa Java 8
+    - name: ja-lang
+      value: Java 8 の機能
+    - name: ko-lang
+      value: Java 8 언어 기능
+    - name: pt-br-lang
+      value: Recursos de linguagem do Java 8
+    - name: ru-lang
+      value: Возможности языка Java 8
+    - name: vi-lang
+      value: Tính năng của Ngôn ngữ Java 8
+    - name: zh-cn-lang
+      value: Java 8 语言功能
+    - name: zh-tw-lang
+      value: Java 8 語言功能
   - title: Android for Work Updates
     path: /preview/features/afw.html
   - title: Scoped Directory Access
     path: /preview/features/scoped-folder-access.html
+    path_attributes:
+    - name: es-lang
+      value: Acceso a directorios determinados
+    - name: ja-lang
+      value: 特定のディレクトリへのアクセス
+    - name: ko-lang
+      value: 범위가 지정된 디렉터리 액세스
+    - name: pt-br-lang
+      value: Acesso a diretórios com escopo
+    - name: ru-lang
+      value: Доступ к выделенным каталогам
+    - name: vi-lang
+      value: Truy cập Thư mục theo Phạm vi
+    - name: zh-cn-lang
+      value: 作用域目录访问
+    - name: zh-tw-lang
+      value: 限定範圍目錄存取
 
 - title: Samples
   path: /preview/samples.html
-  custom_link_attributes:
-  - es-lang="Ejemplos"
-  - ja-lang="サンプル"
-  - ko-lang="샘플"
-  - pt-br-lang="Exemplos"
-  - ru-lang="Примеры"
-  - zh-cn-lang="示例"
-  - zh-tw-lang="範例"
+  path_attributes:
+  - name: es-lang
+    value: Ejemplos
+  - name: in-lang
+    value: Contoh
+  - name: ja-lang
+    value: サンプル
+  - name: ko-lang
+    value: 샘플
+  - name: pt-br-lang
+    value: Exemplos
+  - name: ru-lang
+    value: Примеры
+  - name: zh-cn-lang
+    value: 示例
+  - name: zh-tw-lang
+    value: 範例
 
 - title: License Agreement
   path: /preview/license.html
-  custom_link_attributes:
-  - es-lang="Contrato de licencia"
-  - ja-lang="使用許諾契約"
-  - ko-lang="라이선스 계약"
-  - pt-br-lang="Contrato de licença"
-  - ru-lang="Лицензионное соглашение"
-  - zh-cn-lang="许可协议"
-  - zh-tw-lang="授權協議"
+  path_attributes:
+  - name: es-lang
+    value: Contrato de licencia
+  - name: ja-lang
+    value: 使用許諾契約
+  - name: ko-lang
+    value: 라이선스 계약
+  - name: pt-br-lang
+    value: Contrato de licença
+  - name: ru-lang
+    value: Лицензионное соглашение
+  - name: zh-cn-lang
+    value: 许可协议
+  - name: zh-tw-lang
+    value: 授權協議
diff --git a/docs/html/tools/_book.yaml b/docs/html/tools/_book.yaml
index 5395cc8..fb257f3 100644
--- a/docs/html/tools/_book.yaml
+++ b/docs/html/tools/_book.yaml
@@ -2,10 +2,8 @@
 - title: Download
   path: /sdk/index.html
   section:
-  - title: Installing the SDK
+  - title: Install Android Studio
     path: /sdk/installing/index.html
-  - title: Adding SDK Packages
-    path: /sdk/installing/adding-packages.html
 
 - title: Workflow
   path: /tools/workflow/index.html
@@ -27,13 +25,15 @@
     path: /tools/debugging/index.html
   - title: Publishing
     path: /tools/publishing/publishing_overview.html
-    custom_link_attributes:
-    - zh-cn-lang="发布概述"
+    path_attributes:
+    - name: zh-cn-lang
+      value: 发布概述
     section:
     - title: Preparing for Release
       path: /tools/publishing/preparing.html
-      custom_link_attributes:
-      - zh-cn-lang="准备发布"
+      path_attributes:
+      - name: zh-cn-lang
+        value: 准备发布
     - title: Versioning Your Apps
       path: /tools/publishing/versioning.html
     - title: Signing Your Apps
@@ -65,7 +65,9 @@
       path: /tools/debugging/improving-w-lint.html
     - title: Improving Code Inspection with Annotations
       path: /tools/debugging/annotations.html
-    - title: Deep Link and App Indexing API Support
+    - title: Shrink Your Code and Resources
+      path: /tools/help/proguard.html
+    - title: Supporting URLs and App Indexing in Android Studio
       path: /tools/help/app-link-indexing.html
   - title: UI Tools
     path: /tools/studio/ui-tools.html
@@ -82,6 +84,11 @@
       path: /tools/help/image-asset-studio.html
   - title: AVD Manager
     path: /tools/devices/managing-avds.html
+  - title: Android Emulator
+    path: /tools/devices/emulator.html
+    section:
+    - title: Android Emulator Command-Line Features
+      path: /tools/help/emulator.html
   - title: Debugging Tools
     path: /tools/debugging/debugging-studio.html
     section:
@@ -152,15 +159,8 @@
       path: /tools/debugging/debugging-log.html
   - title: mksdcard
     path: /tools/help/mksdcard.html
-  - title: ProGuard
-    path: /tools/help/proguard.html
   - title: Tracer for OpenGL ES
     path: /tools/help/gltracer.html
-  - title: Virtual Device Emulator
-    path: /tools/devices/emulator.html
-    section:
-    - title: Command Reference
-      path: /tools/help/emulator.html
   - title: zipalign
     path: /tools/help/zipalign.html
 
diff --git a/docs/html/training/_book.yaml b/docs/html/training/_book.yaml
index cd4df43..8f8fab2 100644
--- a/docs/html/training/_book.yaml
+++ b/docs/html/training/_book.yaml
@@ -4,8 +4,9 @@
   section:
   - title: Building Your First App
     path: /training/basics/firstapp/index.html
-    custom_link_attributes:
-    - description="After you've installed the Android SDK, start with this class to learn the basics about Android app development."
+    path_attributes:
+    - name: description
+      value: After you've installed the Android SDK, start with this class to learn the basics about Android app development.
     section:
     - title: Creating an Android Project
       path: /training/basics/firstapp/creating-project.html
@@ -17,8 +18,9 @@
       path: /training/basics/firstapp/starting-activity.html
   - title: Supporting Different Devices
     path: /training/basics/supporting-devices/index.html
-    custom_link_attributes:
-    - description="How to build your app with alternative resources that provide an optimized user experience on multiple device form factors using a single APK."
+    path_attributes:
+    - name: description
+      value: How to build your app with alternative resources that provide an optimized user experience on multiple device form factors using a single APK.
     section:
     - title: Supporting Different Languages
       path: /training/basics/supporting-devices/languages.html
@@ -28,137 +30,215 @@
       path: /training/basics/supporting-devices/platforms.html
   - title: Managing the Activity Lifecycle
     path: /training/basics/activity-lifecycle/index.html
-    custom_link_attributes:
-    - ja-lang="アクティビティのライフサイクル 管理"
-    - ko-lang="액티비티 수명 주기 관리하기"
-    - pt-br-lang="Como gerenciar o ciclo de vida da atividade"
-    - ru-lang="Управление жизненным циклом операций"
-    - zh-cn-lang="管理活动生命周期"
-    - zh-tw-lang="管理應用行為顯示生命週期"
-    - description="How Android activities live and die and how to create a seamless user experience by implementing lifecycle callback methods."
+    path_attributes:
+    - name: ja-lang
+      value: アクティビティのライフサイクル 管理
+    - name: ko-lang
+      value: 액티비티 수명 주기 관리하기
+    - name: pt-br-lang
+      value: Como gerenciar o ciclo de vida da atividade
+    - name: ru-lang
+      value: Управление жизненным циклом операций
+    - name: zh-cn-lang
+      value: 管理活动生命周期
+    - name: zh-tw-lang
+      value: 管理應用行為顯示生命週期
+    - name: description
+      value: How Android activities live and die and how to create a seamless user experience by implementing lifecycle callback methods.
     section:
     - title: Starting an Activity
       path: /training/basics/activity-lifecycle/starting.html
-      custom_link_attributes:
-      - ja-lang="アクティビティを開始する"
-      - ko-lang="액티비티 시작하기"
-      - pt-br-lang="Iniciando uma atividade"
-      - ru-lang="Запуск операции"
-      - zh-cn-lang="开始活动"
-      - zh-tw-lang="啟動應用行為顯示"
+      path_attributes:
+      - name: ja-lang
+        value: アクティビティを開始する
+      - name: ko-lang
+        value: 액티비티 시작하기
+      - name: pt-br-lang
+        value: Iniciando uma atividade
+      - name: ru-lang
+        value: Запуск операции
+      - name: zh-cn-lang
+        value: 开始活动
+      - name: zh-tw-lang
+        value: 啟動應用行為顯示
     - title: Pausing and Resuming an Activity
       path: /training/basics/activity-lifecycle/pausing.html
     - title: Stopping and Restarting an Activity
       path: /training/basics/activity-lifecycle/stopping.html
-      custom_link_attributes:
-      - ja-lang="アクティビティの一時停止と再開"
-      - ko-lang="액티비티 일시정지 및 재개하기"
-      - pt-br-lang="Pausando e reiniciando uma atividade"
-      - ru-lang="Приостановка и возобновление операции"
-      - zh-cn-lang="暂停和继续活动"
-      - zh-tw-lang="暫停並繼續應用行為顯示"
+      path_attributes:
+      - name: ja-lang
+        value: アクティビティの一時停止と再開
+      - name: ko-lang
+        value: 액티비티 일시정지 및 재개하기
+      - name: pt-br-lang
+        value: Pausando e reiniciando uma atividade
+      - name: ru-lang
+        value: Приостановка и возобновление операции
+      - name: zh-cn-lang
+        value: 暂停和继续活动
+      - name: zh-tw-lang
+        value: 暫停並繼續應用行為顯示
     - title: Recreating an Activity
       path: /training/basics/activity-lifecycle/recreating.html
-      custom_link_attributes:
-      - ja-lang="アクティビティを再作成する"
-      - ko-lang="액티비티 재생성하기"
-      - pt-br-lang="Recriando uma atividade"
-      - ru-lang="Воссоздание операции"
-      - zh-cn-lang="重新创建活动"
-      - zh-tw-lang="重新建立應用行為顯示"
+      path_attributes:
+      - name: ja-lang
+        value: アクティビティを再作成する
+      - name: ko-lang
+        value: 액티비티 재생성하기
+      - name: pt-br-lang
+        value: Recriando uma atividade
+      - name: ru-lang
+        value: Воссоздание операции
+      - name: zh-cn-lang
+        value: 重新创建活动
+      - name: zh-tw-lang
+        value: 重新建立應用行為顯示
   - title: Building a Dynamic UI with Fragments
     path: /training/basics/fragments/index.html
-    custom_link_attributes:
-    - description="How to build a user interface for your app that is flexible enough to present multiple UI components on large screens and a more constrained set of UI components on smaller screens—essential for building a single APK for both phones and tablets."
+    path_attributes:
+    - name: description
+      value: How to build a user interface for your app that is flexible enough to present multiple UI components on large screens and a more constrained set of UI components on smaller screens—essential for building a single APK for both phones and tablets.
     section:
     - title: Creating a Fragment
       path: /training/basics/fragments/creating.html
     - title: Building a Flexible UI
       path: /training/basics/fragments/fragment-ui.html
-      custom_link_attributes:
-      - zh-cn-lang="构建灵活的界面"
+      path_attributes:
+      - name: zh-cn-lang
+        value: 构建灵活的界面
     - title: Communicating with Other Fragments
       path: /training/basics/fragments/communicating.html
   - title: Saving Data
     path: /training/basics/data-storage/index.html
-    custom_link_attributes:
-    - ja-lang="データの保存"
-    - ko-lang="데이터 저장하기"
-    - pt-br-lang="Salvando dados"
-    - ru-lang="Сохранение данных"
-    - zh-cn-lang="保存数据"
-    - zh-tw-lang="儲存資料"
-    - description="How to save data on the device, whether it's temporary files, downloaded app assets, user media, structured data, or something else."
+    path_attributes:
+    - name: ja-lang
+      value: データの保存
+    - name: ko-lang
+      value: 데이터 저장하기
+    - name: pt-br-lang
+      value: Salvando dados
+    - name: ru-lang
+      value: Сохранение данных
+    - name: zh-cn-lang
+      value: 保存数据
+    - name: zh-tw-lang
+      value: 儲存資料
+    - name: description
+      value: How to save data on the device, whether it's temporary files, downloaded app assets, user media, structured data, or something else.
     section:
     - title: Saving Key-Value Sets
       path: /training/basics/data-storage/shared-preferences.html
-      custom_link_attributes:
-      - ja-lang="キー値セットを保存する"
-      - ko-lang="키-값 세트 저장하기"
-      - pt-br-lang="Salvando conjuntos de valor-chave"
-      - ru-lang="Сохранение наборов \"ключ-значение\""
-      - zh-cn-lang="保存键值集"
-      - zh-tw-lang="儲存索引鍵值組"
+      path_attributes:
+      - name: ja-lang
+        value: キー値セットを保存する
+      - name: ko-lang
+        value: 키-값 세트 저장하기
+      - name: pt-br-lang
+        value: Salvando conjuntos de valor-chave
+      - name: ru-lang
+        value: Сохранение наборов "ключ-значение"
+      - name: zh-cn-lang
+        value: 保存键值集
+      - name: zh-tw-lang
+        value: 儲存索引鍵值組
     - title: Saving Files
       path: /training/basics/data-storage/files.html
-      custom_link_attributes:
-      - ja-lang="ファイルを保存する"
-      - ko-lang="파일 저장하기"
-      - pt-br-lang="Salvando arquivos"
-      - ru-lang="Сохранение файлов"
-      - zh-cn-lang="保存文件"
-      - zh-tw-lang="儲存檔案"
+      path_attributes:
+      - name: ja-lang
+        value: ファイルを保存する
+      - name: ko-lang
+        value: 파일 저장하기
+      - name: pt-br-lang
+        value: Salvando arquivos
+      - name: ru-lang
+        value: Сохранение файлов
+      - name: zh-cn-lang
+        value: 保存文件
+      - name: zh-tw-lang
+        value: 儲存檔案
     - title: Saving Data in SQL Databases
       path: /training/basics/data-storage/databases.html
-      custom_link_attributes:
-      - ja-lang="SQL データベースにデータを保存する"
-      - ko-lang="SQL 데이터베이스에 데이터 저장하기"
-      - pt-br-lang="Salvando dados em bancos de dados do SQL"
-      - ru-lang="Сохранение данных в базах данных SQL"
-      - zh-cn-lang="在 SQL 数据库中保存数据"
-      - zh-tw-lang="在 SQL 資料庫中儲存資料"
+      path_attributes:
+      - name: ja-lang
+        value: SQL データベースにデータを保存する
+      - name: ko-lang
+        value: SQL 데이터베이스에 데이터 저장하기
+      - name: pt-br-lang
+        value: Salvando dados em bancos de dados do SQL
+      - name: ru-lang
+        value: Сохранение данных в базах данных SQL
+      - name: zh-cn-lang
+        value: 在 SQL 数据库中保存数据
+      - name: zh-tw-lang
+        value: 在 SQL 資料庫中儲存資料
   - title: Interacting with Other Apps
     path: /training/basics/intents/index.html
-    custom_link_attributes:
-    - ja-lang="他のアプリとの相互操作"
-    - ko-lang="액티비티 수명 주기 관리하기"
-    - pt-br-lang="Interagindo com outros aplicativos"
-    - ru-lang="Взаимодействие с другими приложениями"
-    - zh-cn-lang="与其他应用交互"
-    - zh-tw-lang="與其他應用程式互動"
-    - description="How to build a user experience that leverages other apps available on the device to perform advanced user tasks, such as capture a photo or view an address on a map."
+    path_attributes:
+    - name: ja-lang
+      value: 他のアプリとの相互操作
+    - name: ko-lang
+      value: 액티비티 수명 주기 관리하기
+    - name: pt-br-lang
+      value: Interagindo com outros aplicativos
+    - name: ru-lang
+      value: Взаимодействие с другими приложениями
+    - name: zh-cn-lang
+      value: 与其他应用交互
+    - name: zh-tw-lang
+      value: 與其他應用程式互動
+    - name: description
+      value: How to build a user experience that leverages other apps available on the device to perform advanced user tasks, such as capture a photo or view an address on a map.
     section:
     - title: Sending the User to Another App
       path: /training/basics/intents/sending.html
-      custom_link_attributes:
-      - ja-lang="別のアプリにユーザーを送る"
-      - ko-lang="다른 앱으로 사용자 보내기"
-      - pt-br-lang="Enviando o usuário para outro aplicativo"
-      - ru-lang="Направление пользователя в другое приложение"
-      - zh-cn-lang="向另一个应用发送用户"
-      - zh-tw-lang="將使用者傳送至其他應用程式"
+      path_attributes:
+      - name: ja-lang
+        value: 別のアプリにユーザーを送る
+      - name: ko-lang
+        value: 다른 앱으로 사용자 보내기
+      - name: pt-br-lang
+        value: Enviando o usuário para outro aplicativo
+      - name: ru-lang
+        value: Направление пользователя в другое приложение
+      - name: zh-cn-lang
+        value: 向另一个应用发送用户
+      - name: zh-tw-lang
+        value: 將使用者傳送至其他應用程式
     - title: Getting a Result from the Activity
       path: /training/basics/intents/result.html
-      custom_link_attributes:
-      - ja-lang="アクティビティから結果を取得する"
-      - ko-lang="액티비티로부터 결과 가져오기"
-      - pt-br-lang="Obtendo resultados de uma atividade"
-      - ru-lang="Получение результата операции"
-      - zh-cn-lang="获取活动的结果"
-      - zh-tw-lang="從應用行為顯示取得結果"
+      path_attributes:
+      - name: ja-lang
+        value: アクティビティから結果を取得する
+      - name: ko-lang
+        value: 액티비티로부터 결과 가져오기
+      - name: pt-br-lang
+        value: Obtendo resultados de uma atividade
+      - name: ru-lang
+        value: Получение результата операции
+      - name: zh-cn-lang
+        value: 获取活动的结果
+      - name: zh-tw-lang
+        value: 從應用行為顯示取得結果
     - title: Allowing Other Apps to Start Your Activity
       path: /training/basics/intents/filters.html
-      custom_link_attributes:
-      - ja-lang="他のアプリからのアクティビティの開始を許可する"
-      - ko-lang="다른 앱이 자신의 액티비티를 시작하도록 허용하기"
-      - pt-br-lang="Permitindo que outros aplicativos iniciem sua atividade"
-      - ru-lang="Разрешение другим приложениям на запуск вашей операции"
-      - zh-cn-lang="允许其他应用开始您的活动"
-      - zh-tw-lang="允許其他應用程式啟動您的應用行為顯示"
+      path_attributes:
+      - name: ja-lang
+        value: 他のアプリからのアクティビティの開始を許可する
+      - name: ko-lang
+        value: 다른 앱이 자신의 액티비티를 시작하도록 허용하기
+      - name: pt-br-lang
+        value: Permitindo que outros aplicativos iniciem sua atividade
+      - name: ru-lang
+        value: Разрешение другим приложениям на запуск вашей операции
+      - name: zh-cn-lang
+        value: 允许其他应用开始您的活动
+      - name: zh-tw-lang
+        value: 允許其他應用程式啟動您的應用行為顯示
   - title: Working with System Permissions
     path: /training/permissions/index.html
-    custom_link_attributes:
-    - description="How to declare that your app needs access to features and resources outside of its 'sandbox', and how to request those privileges at runtime."
+    path_attributes:
+    - name: description
+      value: How to declare that your app needs access to features and resources outside of its 'sandbox', and how to request those privileges at runtime.
     section:
     - title: Declaring Permissions
       path: /training/permissions/declaring.html
@@ -172,8 +252,9 @@
   section:
   - title: Sharing Simple Data
     path: /training/sharing/index.html
-    custom_link_attributes:
-    - description="How to take your app interaction to the next level by sharing information with other apps, receive information back, and provide a simple and scalable way to perform Share actions with user content."
+    path_attributes:
+    - name: description
+      value: How to take your app interaction to the next level by sharing information with other apps, receive information back, and provide a simple and scalable way to perform Share actions with user content.
     section:
     - title: Sending Simple Data to Other Apps
       path: /training/sharing/send.html
@@ -183,8 +264,9 @@
       path: /training/sharing/shareaction.html
   - title: Sharing Files
     path: /training/secure-file-sharing/index.html
-    custom_link_attributes:
-    - description="How to provide secure access to a file associated with your app using a content URI and temporary access permissions."
+    path_attributes:
+    - name: description
+      value: How to provide secure access to a file associated with your app using a content URI and temporary access permissions.
     section:
     - title: Setting Up File Sharing
       path: /training/secure-file-sharing/setup-sharing.html
@@ -196,8 +278,9 @@
       path: /training/secure-file-sharing/retrieve-info.html
   - title: Sharing Files with NFC
     path: /training/beam-files/index.html
-    custom_link_attributes:
-    - description="How to transfer files between devices using the NFC Android Beam feature."
+    path_attributes:
+    - name: description
+      value: How to transfer files between devices using the NFC Android Beam feature.
     section:
     - title: Sending Files to Another Device
       path: /training/beam-files/send-files.html
@@ -209,8 +292,9 @@
   section:
   - title: Managing Audio Playback
     path: /training/managing-audio/index.html
-    custom_link_attributes:
-    - description="How to respond to hardware audio key presses, request audio focus when playing audio, and respond appropriately to changes in audio focus."
+    path_attributes:
+    - name: description
+      value: How to respond to hardware audio key presses, request audio focus when playing audio, and respond appropriately to changes in audio focus.
     section:
     - title: Controlling Your App's Volume and Playback
       path: /training/managing-audio/volume-playback.html
@@ -220,8 +304,9 @@
       path: /training/managing-audio/audio-output.html
   - title: Capturing Photos
     path: /training/camera/index.html
-    custom_link_attributes:
-    - description="How to leverage existing camera apps on the user's device to capture photos or control the camera hardware directly and build your own camera app."
+    path_attributes:
+    - name: description
+      value: How to leverage existing camera apps on the user's device to capture photos or control the camera hardware directly and build your own camera app.
     section:
     - title: Taking Photos Simply
       path: /training/camera/photobasics.html
@@ -231,8 +316,9 @@
       path: /training/camera/cameradirect.html
   - title: Printing Content
     path: /training/printing/index.html
-    custom_link_attributes:
-    - description="How to print photos, HTML documents, and custom documents from your app."
+    path_attributes:
+    - name: description
+      value: How to print photos, HTML documents, and custom documents from your app.
     section:
     - title: Photos
       path: /training/printing/photos.html
@@ -246,8 +332,9 @@
   section:
   - title: Displaying Bitmaps Efficiently
     path: /training/displaying-bitmaps/index.html
-    custom_link_attributes:
-    - description="How to load and process bitmaps while keeping your user interface responsive and avoid exceeding memory limits."
+    path_attributes:
+    - name: description
+      value: How to load and process bitmaps while keeping your user interface responsive and avoid exceeding memory limits.
     section:
     - title: Loading Large Bitmaps Efficiently
       path: /training/displaying-bitmaps/load-bitmap.html
@@ -261,8 +348,9 @@
       path: /training/displaying-bitmaps/display-bitmap.html
   - title: Displaying Graphics with OpenGL ES
     path: /training/graphics/opengl/index.html
-    custom_link_attributes:
-    - description="How to create OpenGL graphics within the Android app framework and respond to touch input."
+    path_attributes:
+    - name: description
+      value: How to create OpenGL graphics within the Android app framework and respond to touch input.
     section:
     - title: Building an OpenGL ES Environment
       path: /training/graphics/opengl/environment.html
@@ -278,8 +366,9 @@
       path: /training/graphics/opengl/touch.html
   - title: Animating Views Using Scenes and Transitions
     path: /training/transitions/index.html
-    custom_link_attributes:
-    - description="How to animate state changes in a view hierarchy using transitions."
+    path_attributes:
+    - name: description
+      value: How to animate state changes in a view hierarchy using transitions.
     section:
     - title: The Transitions Framework
       path: /training/transitions/overview.html
@@ -291,8 +380,9 @@
       path: /training/transitions/custom-transitions.html
   - title: Adding Animations
     path: /training/animation/index.html
-    custom_link_attributes:
-    - description="How to add transitional animations to your user interface."
+    path_attributes:
+    - name: description
+      value: How to add transitional animations to your user interface.
     section:
     - title: Crossfading Two Views
       path: /training/animation/crossfade.html
@@ -310,8 +400,9 @@
   section:
   - title: Connecting Devices Wirelessly
     path: /training/connect-devices-wirelessly/index.html
-    custom_link_attributes:
-    - description="How to find and connect to local devices using Network Service Discovery and how to create peer-to-peer connections with Wi-Fi."
+    path_attributes:
+    - name: description
+      value: How to find and connect to local devices using Network Service Discovery and how to create peer-to-peer connections with Wi-Fi.
     section:
     - title: Using Network Service Discovery
       path: /training/connect-devices-wirelessly/nsd.html
@@ -321,8 +412,9 @@
       path: /training/connect-devices-wirelessly/nsd-wifi-direct.html
   - title: Performing Network Operations
     path: /training/basics/network-ops/index.html
-    custom_link_attributes:
-    - description="How to create a network connection, monitor the connection for changes in connectivity, and perform transactions with XML data."
+    path_attributes:
+    - name: description
+      value: How to create a network connection, monitor the connection for changes in connectivity, and perform transactions with XML data.
     section:
     - title: Connecting to the Network
       path: /training/basics/network-ops/connecting.html
@@ -332,8 +424,9 @@
       path: /training/basics/network-ops/xml.html
   - title: Transferring Data Without Draining the Battery
     path: /training/efficient-downloads/index.html
-    custom_link_attributes:
-    - description="How to minimize your app's impact on the battery when performing downloads and other network transactions."
+    path_attributes:
+    - name: description
+      value: How to minimize your app's impact on the battery when performing downloads and other network transactions.
     section:
     - title: Optimizing Downloads for Efficient Network Access
       path: /training/efficient-downloads/efficient-network-access.html
@@ -345,8 +438,9 @@
       path: /training/efficient-downloads/connectivity_patterns.html
   - title: Syncing to the Cloud
     path: /training/backup/index.html
-    custom_link_attributes:
-    - description="How to sync and back up app and user data to remote web services in the cloud and how to restore the data back to multiple devices."
+    path_attributes:
+    - name: description
+      value: How to sync and back up app and user data to remote web services in the cloud and how to restore the data back to multiple devices.
     section:
     - title: Configuring Auto Backup
       path: /training/backup/autosyncapi.html
@@ -354,12 +448,14 @@
       path: /training/backup/backupapi.html
   - title: Resolving Cloud Save Conflicts
     path: /training/cloudsave/conflict-res.html
-    custom_link_attributes:
-    - description="How to design a robust conflict resolution strategy for apps that save data to the cloud."
+    path_attributes:
+    - name: description
+      value: How to design a robust conflict resolution strategy for apps that save data to the cloud.
   - title: Transferring Data Using Sync Adapters
     path: /training/sync-adapters/index.html
-    custom_link_attributes:
-    - description="How to transfer data between the cloud and the device using the Android sync adapter framework"
+    path_attributes:
+    - name: description
+      value: How to transfer data between the cloud and the device using the Android sync adapter framework
     section:
     - title: Creating a Stub Authenticator
       path: /training/sync-adapters/creating-authenticator.html
@@ -371,8 +467,9 @@
       path: /training/sync-adapters/running-sync-adapter.html
   - title: Transmitting Network Data Using Volley
     path: /training/volley/index.html
-    custom_link_attributes:
-    - description="How to perform fast, scalable UI operations over the network using Volley"
+    path_attributes:
+    - name: description
+      value: How to perform fast, scalable UI operations over the network using Volley
     section:
     - title: Sending a Simple Request
       path: /training/volley/simple.html
@@ -388,8 +485,9 @@
   section:
   - title: Making Your App Location-Aware
     path: /training/location/index.html
-    custom_link_attributes:
-    - description="How to add location-aware features to your app by getting the user's current location."
+    path_attributes:
+    - name: description
+      value: How to add location-aware features to your app by getting the user's current location.
     section:
     - title: Getting the Last Known Location
       path: /training/location/retrieve-current.html
@@ -403,16 +501,18 @@
       path: /training/location/geofencing.html
   - title: Adding Maps
     path: /training/maps/index.html
-    custom_link_attributes:
-    - description="How to add maps and mapping information to your app."
+    path_attributes:
+    - name: description
+      value: How to add maps and mapping information to your app.
 
 - title: Building Apps with User Info & Sign-In
   path: /training/building-userinfo.html
   section:
   - title: Accessing Contacts Data
     path: /training/contacts-provider/index.html
-    custom_link_attributes:
-    - description="How to use Android's central address book, the Contacts Provider, to display contacts and their details and modify contact information."
+    path_attributes:
+    - name: description
+      value: How to use Android's central address book, the Contacts Provider, to display contacts and their details and modify contact information.
     section:
     - title: Retrieving a List of Contacts
       path: /training/contacts-provider/retrieve-names.html
@@ -424,16 +524,18 @@
       path: /training/contacts-provider/display-contact-badge.html
   - title: Adding Sign-In
     path: /training/sign-in/index.html
-    custom_link_attributes:
-    - description="How to add user sign-in functionality to your app."
+    path_attributes:
+    - name: description
+      value: How to add user sign-in functionality to your app.
 
 - title: Building Apps for Wearables
   path: /training/building-wearables.html
   section:
   - title: Adding Wearable Features to Notifications
     path: /training/wearables/notifications/index.html
-    custom_link_attributes:
-    - description="How to build handheld notifications that are synced to and look great on wearables."
+    path_attributes:
+    - name: description
+      value: How to build handheld notifications that are synced to and look great on wearables.
     section:
     - title: Creating a Notification
       path: /training/wearables/notifications/creating.html
@@ -445,8 +547,9 @@
       path: /training/wearables/notifications/stacks.html
   - title: Creating Wearable Apps
     path: /training/wearables/apps/index.html
-    custom_link_attributes:
-    - description="How to build apps that run directly on wearables."
+    path_attributes:
+    - name: description
+      value: How to build apps that run directly on wearables.
     section:
     - title: Creating and Running a Wearable App
       path: /training/wearables/apps/creating.html
@@ -462,8 +565,9 @@
       path: /training/wearables/apps/bt-debugging.html
   - title: Creating Custom UIs
     path: /training/wearables/ui/index.html
-    custom_link_attributes:
-    - description="How to create custom user interfaces for wearable apps."
+    path_attributes:
+    - name: description
+      value: How to create custom user interfaces for wearable apps.
     section:
     - title: Defining Layouts
       path: /training/wearables/ui/layouts.html
@@ -479,8 +583,9 @@
       path: /training/wearables/ui/exit.html
   - title: Sending and Syncing Data
     path: /training/wearables/data-layer/index.html
-    custom_link_attributes:
-    - description="How to sync data between handhelds and wearables."
+    path_attributes:
+    - name: description
+      value: How to sync data between handhelds and wearables.
     section:
     - title: Accessing the Wearable Data Layer
       path: /training/wearables/data-layer/accessing.html
@@ -494,8 +599,9 @@
       path: /training/wearables/data-layer/events.html
   - title: Creating Watch Faces
     path: /training/wearables/watch-faces/index.html
-    custom_link_attributes:
-    - description="How to create watch faces for wearables."
+    path_attributes:
+    - name: description
+      value: How to create watch faces for wearables.
     section:
     - title: Designing Watch Faces
       path: /training/wearables/watch-faces/designing.html
@@ -515,70 +621,85 @@
       path: /training/wearables/watch-faces/performance.html
   - title: Detecting Location
     path: /training/articles/wear-location-detection.html
-    custom_link_attributes:
-    - description="How to detect location data on Android Wear devices."
+    path_attributes:
+    - name: description
+      value: How to detect location data on Android Wear devices.
   - title: Requesting Permissions
     path: /training/articles/wear-permissions.html
-    custom_link_attributes:
-    - description="How to request permissions on Android Wear devices."
+    path_attributes:
+    - name: description
+      value: How to request permissions on Android Wear devices.
   - title: Using the Speaker
     path: /training/wearables/wearable-sounds.html
-    custom_link_attributes:
-    - description="How to use the speaker on Android Wear devices."
+    path_attributes:
+    - name: description
+      value: How to use the speaker on Android Wear devices.
 
 - title: Building Apps for TV
   path: /training/tv/index.html
   section:
   - title: Building TV Apps
     path: /training/tv/start/index.html
-    custom_link_attributes:
-    - ja-lang="TV アプリのビルド"
-    - description="How to start building TV apps or extend your existing app to run on TV devices."
+    path_attributes:
+    - name: ja-lang
+      value: TV アプリのビルド
+    - name: description
+      value: How to start building TV apps or extend your existing app to run on TV devices.
     section:
     - title: Getting Started with TV Apps
       path: /training/tv/start/start.html
-      custom_link_attributes:
-      - ja-lang="TV アプリのビルドを開始する"
+      path_attributes:
+      - name: ja-lang
+        value: TV アプリのビルドを開始する
     - title: Handling TV Hardware
       path: /training/tv/start/hardware.html
-      custom_link_attributes:
-      - ja-lang="TV ハードウェアを処理する"
+      path_attributes:
+      - name: ja-lang
+        value: TV ハードウェアを処理する
     - title: Building TV Layouts
       path: /training/tv/start/layouts.html
-      custom_link_attributes:
-      - ja-lang="TV 向けレイアウトをビルドする"
+      path_attributes:
+      - name: ja-lang
+        value: TV 向けレイアウトをビルドする
     - title: Creating TV Navigation
       path: /training/tv/start/navigation.html
-      custom_link_attributes:
-      - ja-lang="TV 用のナビゲーションを作成する"
+      path_attributes:
+      - name: ja-lang
+        value: TV 用のナビゲーションを作成する
   - title: Building TV Playback Apps
     path: /training/tv/playback/index.html
-    custom_link_attributes:
-    - ja-lang="TV 再生アプリのビルド"
-    - description="How to build apps that provide media catalogs and play content."
+    path_attributes:
+    - name: ja-lang
+      value: TV 再生アプリのビルド
+    - name: description
+      value: How to build apps that provide media catalogs and play content.
     section:
     - title: Creating a Catalog Browser
       path: /training/tv/playback/browse.html
-      custom_link_attributes:
-      - ja-lang="カタログ ブラウザを作成する"
+      path_attributes:
+      - name: ja-lang
+        value: カタログ ブラウザを作成する
     - title: Providing a Card View
       path: /training/tv/playback/card.html
     - title: Building a Details View
       path: /training/tv/playback/details.html
-      custom_link_attributes:
-      - ja-lang="詳細ビューをビルドする"
+      path_attributes:
+      - name: ja-lang
+        value: 詳細ビューをビルドする
     - title: Displaying a Now Playing Card
       path: /training/tv/playback/now-playing.html
-      custom_link_attributes:
-      - ja-lang="再生中カードを表示する"
+      path_attributes:
+      - name: ja-lang
+        value: 再生中カードを表示する
     - title: Adding a Guided Step
       path: /training/tv/playback/guided-step.html
     - title: Enabling Background Playback
       path: /training/tv/playback/options.html
   - title: Helping Users Find Content on TV
     path: /training/tv/discovery/index.html
-    custom_link_attributes:
-    - description="How to help users discover content from your app."
+    path_attributes:
+    - name: description
+      value: How to help users discover content from your app.
     section:
     - title: Recommending TV Content
       path: /training/tv/discovery/recommendations.html
@@ -588,12 +709,14 @@
       path: /training/tv/discovery/in-app-search.html
   - title: Building TV Games
     path: /training/tv/games/index.html
-    custom_link_attributes:
-    - description="How to build games for TV."
+    path_attributes:
+    - name: description
+      value: How to build games for TV.
   - title: Building TV Channels
     path: /training/tv/tif/index.html
-    custom_link_attributes:
-    - description="How to build channels for TV."
+    path_attributes:
+    - name: description
+      value: How to build channels for TV.
     section:
     - title: Developing a TV Input Service
       path: /training/tv/tif/tvinput.html
@@ -603,24 +726,28 @@
       path: /training/tv/tif/ui.html
   - title: TV Apps Checklist
     path: /training/tv/publishing/checklist.html
-    custom_link_attributes:
-    - description="An itemized list of requirements for TV apps."
+    path_attributes:
+    - name: description
+      value: An itemized list of requirements for TV apps.
 
 - title: Building Apps for Auto
   path: /training/auto/index.html
   section:
   - title: Getting Started with Auto
     path: /training/auto/start/index.html
-    custom_link_attributes:
-    - description="How to start building or extending apps that work with Auto devices."
+    path_attributes:
+    - name: description
+      value: How to start building or extending apps that work with Auto devices.
   - title: Playing Audio for Auto
     path: /training/auto/audio/index.html
-    custom_link_attributes:
-    - description="How to extend audio apps to play content on Auto devices."
+    path_attributes:
+    - name: description
+      value: How to extend audio apps to play content on Auto devices.
   - title: Messaging for Auto
     path: /training/auto/messaging/index.html
-    custom_link_attributes:
-    - description="How to extend text messaging apps to work with Auto devices."
+    path_attributes:
+    - name: description
+      value: How to extend text messaging apps to work with Auto devices.
 
 - title: Building Apps for Work
   path: /training/enterprise/index.html
@@ -639,8 +766,9 @@
   section:
   - title: Designing Effective Navigation
     path: /training/design-navigation/index.html
-    custom_link_attributes:
-    - description="How to plan your app's screen hierarchy and forms of navigation so users can effectively and intuitively traverse your app content using various navigation patterns."
+    path_attributes:
+    - name: description
+      value: How to plan your app's screen hierarchy and forms of navigation so users can effectively and intuitively traverse your app content using various navigation patterns.
     section:
     - title: Planning Screens and Their Relationships
       path: /training/design-navigation/screen-planning.html
@@ -654,8 +782,9 @@
       path: /training/design-navigation/wireframing.html
   - title: Implementing Effective Navigation
     path: /training/implementing-navigation/index.html
-    custom_link_attributes:
-    - description="How to implement various navigation patterns such as swipe views, a navigation drawer, and up navigation."
+    path_attributes:
+    - name: description
+      value: How to implement various navigation patterns such as swipe views, a navigation drawer, and up navigation.
     section:
     - title: Creating Swipe Views with Tabs
       path: /training/implementing-navigation/lateral.html
@@ -669,8 +798,9 @@
       path: /training/implementing-navigation/descendant.html
   - title: Notifying the User
     path: /training/notify-user/index.html
-    custom_link_attributes:
-    - description="How to display messages called notifications outside of your application's UI."
+    path_attributes:
+    - name: description
+      value: How to display messages called notifications outside of your application's UI.
     section:
     - title: Building a Notification
       path: /training/notify-user/build-notification.html
@@ -684,8 +814,9 @@
       path: /training/notify-user/display-progress.html
   - title: Supporting Swipe-to-Refresh
     path: /training/swipe/index.html
-    custom_link_attributes:
-    - description="How to modify your app's layout to support manual content updates triggered by the swipe-to-refresh gesture."
+    path_attributes:
+    - name: description
+      value: How to modify your app's layout to support manual content updates triggered by the swipe-to-refresh gesture.
     section:
     - title: Adding Swipe-to-Refresh To Your App
       path: /training/swipe/add-swipe-interface.html
@@ -693,8 +824,9 @@
       path: /training/swipe/respond-refresh-request.html
   - title: Adding Search Functionality
     path: /training/search/index.html
-    custom_link_attributes:
-    - description="How to properly add a search interface to your app and create a searchable database."
+    path_attributes:
+    - name: description
+      value: How to properly add a search interface to your app and create a searchable database.
     section:
     - title: Setting up the Search Interface
       path: /training/search/setup.html
@@ -704,8 +836,9 @@
       path: /training/search/backward-compat.html
   - title: Making Your App Content Searchable by Google
     path: /training/app-indexing/index.html
-    custom_link_attributes:
-    - description="How to enable deep linking and indexing of your application content so that users can open this content directly from their mobile search results."
+    path_attributes:
+    - name: description
+      value: How to enable deep linking and indexing of your application content so that users can open this content directly from their mobile search results.
     section:
     - title: Enabling Deep Links for App Content
       path: /training/app-indexing/deep-linking.html
@@ -713,47 +846,64 @@
       path: /training/app-indexing/enabling-app-indexing.html
   - title: Optimizing Content for the Assistant
     path: /training/articles/assistant.html
-    custom_link_attributes:
-    - description="Support contextually relevant actions through the Assist API."
+    path_attributes:
+    - name: description
+      value: Support contextually relevant actions through the Assist API.
   - title: Handling App Links
     path: /training/app-links/index.html
-    custom_link_attributes:
-    - description="How to enable the system to handle web requests by taking the user directly to your app instead of your website."
+    path_attributes:
+    - name: description
+      value: How to enable the system to handle web requests by taking the user directly to your app instead of your website.
 
 - title: Best Practices for User Interface
   path: /training/best-ui.html
   section:
   - title: Designing for Multiple Screens
     path: /training/multiscreen/index.html
-    custom_link_attributes:
-    - es-lang="Cómo diseñar aplicaciones para varias pantallas"
-    - ja-lang="複数画面のデザイン"
-    - zh-cn-lang="针对多种屏幕进行设计"
-    - description="How to build a user interface that's flexible enough to fit perfectly on any screen and how to create different interaction patterns that are optimized for different screen sizes."
+    path_attributes:
+    - name: es-lang
+      value: Cómo diseñar aplicaciones para varias pantallas
+    - name: ja-lang
+      value: 複数画面のデザイン
+    - name: zh-cn-lang
+      value: 针对多种屏幕进行设计
+    - name: description
+      value: How to build a user interface that's flexible enough to fit perfectly on any screen and how to create different interaction patterns that are optimized for different screen sizes.
     section:
     - title: Supporting Different Screen Sizes
       path: /training/multiscreen/screensizes.html
-      custom_link_attributes:
-      - es-lang="Cómo admitir varios tamaños de pantalla"
-      - ja-lang="さまざまな画面サイズのサポート"
-      - ko-lang="다양한 화면 크기 지원"
-      - zh-cn-lang="支持各种屏幕尺寸"
+      path_attributes:
+      - name: es-lang
+        value: Cómo admitir varios tamaños de pantalla
+      - name: ja-lang
+        value: さまざまな画面サイズのサポート
+      - name: ko-lang
+        value: 다양한 화면 크기 지원
+      - name: zh-cn-lang
+        value: 支持各种屏幕尺寸
     - title: Supporting Different Screen Densities
       path: /training/multiscreen/screendensities.html
-      custom_link_attributes:
-      - es-lang="Cómo admitir varias densidades de pantalla"
-      - ja-lang="さまざまな画面密度のサポート"
-      - zh-cn-lang="支持各种屏幕密度"
+      path_attributes:
+      - name: es-lang
+        value: Cómo admitir varias densidades de pantalla
+      - name: ja-lang
+        value: さまざまな画面密度のサポート
+      - name: zh-cn-lang
+        value: 支持各种屏幕密度
     - title: Implementing Adaptive UI Flows
       path: /training/multiscreen/adaptui.html
-      custom_link_attributes:
-      - es-lang="Cómo implementar interfaces de usuario adaptables"
-      - ja-lang="順応性のある UI フローの実装"
-      - zh-cn-lang="实施自适应用户界面流程"
+      path_attributes:
+      - name: es-lang
+        value: Cómo implementar interfaces de usuario adaptables
+      - name: ja-lang
+        value: 順応性のある UI フローの実装
+      - name: zh-cn-lang
+        value: 实施自适应用户界面流程
   - title: Adding the App Bar
     path: /training/appbar/index.html
-    custom_link_attributes:
-    - description="How to use the support library's toolbar widget to implement an app bar that displays properly on a wide range of devices."
+    path_attributes:
+    - name: description
+      value: How to use the support library's toolbar widget to implement an app bar that displays properly on a wide range of devices.
     section:
     - title: Setting Up the App Bar
       path: /training/appbar/setting-up.html
@@ -765,8 +915,9 @@
       path: /training/appbar/action-views.html
   - title: Showing Pop-Up Messages
     path: /training/snackbar/index.html
-    custom_link_attributes:
-    - description="How to use the support library's Snackbar widget to display a brief pop-up message."
+    path_attributes:
+    - name: description
+      value: How to use the support library's Snackbar widget to display a brief pop-up message.
     section:
     - title: Building and Displaying a Pop-Up Message
       path: /training/snackbar/showing.html
@@ -774,8 +925,9 @@
       path: /training/snackbar/action.html
   - title: Creating Custom Views
     path: /training/custom-views/index.html
-    custom_link_attributes:
-    - description="How to build custom UI widgets that are interactive and smooth."
+    path_attributes:
+    - name: description
+      value: How to build custom UI widgets that are interactive and smooth.
     section:
     - title: Creating a Custom View Class
       path: /training/custom-views/create-view.html
@@ -787,8 +939,9 @@
       path: /training/custom-views/optimizing-view.html
   - title: Creating Backward-Compatible UIs
     path: /training/backward-compatible-ui/index.html
-    custom_link_attributes:
-    - description="How to use UI components and other APIs from the more recent versions of Android while remaining compatible with older versions of the platform."
+    path_attributes:
+    - name: description
+      value: How to use UI components and other APIs from the more recent versions of Android while remaining compatible with older versions of the platform.
     section:
     - title: Abstracting the New APIs
       path: /training/backward-compatible-ui/abstracting.html
@@ -800,8 +953,9 @@
       path: /training/backward-compatible-ui/using-component.html
   - title: Implementing Accessibility
     path: /training/accessibility/index.html
-    custom_link_attributes:
-    - description="How to make your app accessible to users with vision impairment or other physical disabilities."
+    path_attributes:
+    - name: description
+      value: How to make your app accessible to users with vision impairment or other physical disabilities.
     section:
     - title: Developing Accessible Applications
       path: /training/accessibility/accessible-app.html
@@ -811,8 +965,9 @@
       path: /training/accessibility/testing.html
   - title: Managing the System UI
     path: /training/system-ui/index.html
-    custom_link_attributes:
-    - description="How to hide and show status and navigation bars across different versions of Android, while managing the display of other screen components."
+    path_attributes:
+    - name: description
+      value: How to hide and show status and navigation bars across different versions of Android, while managing the display of other screen components.
     section:
     - title: Dimming the System Bars
       path: /training/system-ui/dim.html
@@ -826,110 +981,184 @@
       path: /training/system-ui/visibility.html
   - title: Creating Apps with Material Design
     path: /training/material/index.html
-    custom_link_attributes:
-    - es-lang="Crear aplicaciones con Material Design"
-    - in-lang="Desain Bahan untuk Pengembang"
-    - ja-lang="マテリアル デザインでのアプリ作成"
-    - ko-lang="개발자를 위한 머티리얼 디자인"
-    - pt-br-lang="Material Design para desenvolvedores"
-    - ru-lang="Создание приложений с помощью Material Design"
-    - vi-lang="Material Design cho Nhà phát triển"
-    - zh-cn-lang="面向开发者的材料设计"
-    - zh-tw-lang="開發人員材料設計"
-    - description="How to implement material design on Android."
+    path_attributes:
+    - name: es-lang
+      value: Crear aplicaciones con Material Design
+    - name: in-lang
+      value: Desain Bahan untuk Pengembang
+    - name: ja-lang
+      value: マテリアル デザインでのアプリ作成
+    - name: ko-lang
+      value: 개발자를 위한 머티리얼 디자인
+    - name: pt-br-lang
+      value: Material Design para desenvolvedores
+    - name: ru-lang
+      value: Создание приложений с помощью Material Design
+    - name: vi-lang
+      value: Material Design cho Nhà phát triển
+    - name: zh-cn-lang
+      value: 面向开发者的材料设计
+    - name: zh-tw-lang
+      value: 開發人員材料設計
+    - name: description
+      value: How to implement material design on Android.
     section:
     - title: Getting Started
       path: /training/material/get-started.html
-      custom_link_attributes:
-      - es-lang="Comencemos"
-      - in-lang="Memulai"
-      - ja-lang="スタート ガイド"
-      - ko-lang="시작하기"
-      - pt-br-lang="Como iniciar"
-      - ru-lang="Начало работы"
-      - vi-lang="Bắt đầu"
-      - zh-cn-lang="入门指南"
-      - zh-tw-lang="開始使用"
+      path_attributes:
+      - name: es-lang
+        value: Comencemos
+      - name: in-lang
+        value: Memulai
+      - name: ja-lang
+        value: スタート ガイド
+      - name: ko-lang
+        value: 시작하기
+      - name: pt-br-lang
+        value: Como iniciar
+      - name: ru-lang
+        value: Начало работы
+      - name: vi-lang
+        value: Bắt đầu
+      - name: zh-cn-lang
+        value: 入门指南
+      - name: zh-tw-lang
+        value: 開始使用
     - title: Using the Material Theme
       path: /training/material/theme.html
-      custom_link_attributes:
-      - es-lang="Usar el tema Material"
-      - in-lang="Menggunakan Tema Bahan"
-      - ja-lang="マテリアル テーマの使用"
-      - ko-lang="머티어리얼 테마 사용"
-      - pt-br-lang="Como usar o tema do Material"
-      - ru-lang="Использование темы Material Design"
-      - vi-lang="Sử dụng Chủ đề Material"
-      - zh-cn-lang="使用材料主题"
-      - zh-tw-lang="使用材料設計風格"
+      path_attributes:
+      - name: es-lang
+        value: Usar el tema Material
+      - name: in-lang
+        value: Menggunakan Tema Bahan
+      - name: ja-lang
+        value: マテリアル テーマの使用
+      - name: ko-lang
+        value: 머티어리얼 테마 사용
+      - name: pt-br-lang
+        value: Como usar o tema do Material
+      - name: ru-lang
+        value: Использование темы Material Design
+      - name: vi-lang
+        value: Sử dụng Chủ đề Material
+      - name: zh-cn-lang
+        value: 使用材料主题
+      - name: zh-tw-lang
+        value: 使用材料設計風格
     - title: Creating Lists and Cards
       path: /training/material/lists-cards.html
-      custom_link_attributes:
-      - es-lang="Crear listas y tarjetas"
-      - in-lang="Membuat Daftar dan Kartu"
-      - ja-lang="リストとカードの作成"
-      - ko-lang="목록 및 카드 생성"
-      - pt-br-lang="Como criar listas e cartões"
-      - ru-lang="Создание списков и подсказок"
-      - vi-lang="Tạo Danh sách và Thẻ"
-      - zh-cn-lang="创建列表与卡片"
-      - zh-tw-lang="建立清單和卡片"
+      path_attributes:
+      - name: es-lang
+        value: Crear listas y tarjetas
+      - name: in-lang
+        value: Membuat Daftar dan Kartu
+      - name: ja-lang
+        value: リストとカードの作成
+      - name: ko-lang
+        value: 목록 및 카드 생성
+      - name: pt-br-lang
+        value: Como criar listas e cartões
+      - name: ru-lang
+        value: Создание списков и подсказок
+      - name: vi-lang
+        value: Tạo Danh sách và Thẻ
+      - name: zh-cn-lang
+        value: 创建列表与卡片
+      - name: zh-tw-lang
+        value: 建立清單和卡片
     - title: Defining Shadows and Clipping Views
       path: /training/material/shadows-clipping.html
-      custom_link_attributes:
-      - es-lang="Definir vistas de recorte y sombras"
-      - in-lang="Mendefinisikan Bayangan dan Memangkas Tampilan"
-      - ja-lang="シャドウとクリッピング ビューの定義"
-      - ko-lang="그림자 정의 및 뷰 클리핑"
-      - pt-br-lang="Como definir sombras e recortar visualizações"
-      - ru-lang="Определение теней и обрезка представлений"
-      - vi-lang="Định nghĩa Đổ bóng và Dạng xem Cắt hình"
-      - zh-cn-lang="定义阴影与裁剪视图"
-      - zh-tw-lang="定義陰影和裁剪檢視"
+      path_attributes:
+      - name: es-lang
+        value: Definir vistas de recorte y sombras
+      - name: in-lang
+        value: Mendefinisikan Bayangan dan Memangkas Tampilan
+      - name: ja-lang
+        value: シャドウとクリッピング ビューの定義
+      - name: ko-lang
+        value: 그림자 정의 및 뷰 클리핑
+      - name: pt-br-lang
+        value: Como definir sombras e recortar visualizações
+      - name: ru-lang
+        value: Определение теней и обрезка представлений
+      - name: vi-lang
+        value: Định nghĩa Đổ bóng và Dạng xem Cắt hình
+      - name: zh-cn-lang
+        value: 定义阴影与裁剪视图
+      - name: zh-tw-lang
+        value: 定義陰影和裁剪檢視
     - title: Working with Drawables
       path: /training/material/drawables.html
-      custom_link_attributes:
-      - es-lang="Trabajar con interfaces dibujables"
-      - in-lang="Bekerja dengan Drawable"
-      - ja-lang="ドローアブルの使用"
-      - ko-lang="Drawable 사용"
-      - pt-br-lang="Como trabalhar com desenháveis"
-      - ru-lang="Работа с элементами дизайна"
-      - vi-lang="Làm việc với Nội dung vẽ được"
-      - zh-cn-lang="使用 Drawables"
-      - zh-tw-lang="使用可繪項目"
+      path_attributes:
+      - name: es-lang
+        value: Trabajar con interfaces dibujables
+      - name: in-lang
+        value: Bekerja dengan Drawable
+      - name: ja-lang
+        value: ドローアブルの使用
+      - name: ko-lang
+        value: Drawable 사용
+      - name: pt-br-lang
+        value: Como trabalhar com desenháveis
+      - name: ru-lang
+        value: Работа с элементами дизайна
+      - name: vi-lang
+        value: Làm việc với Nội dung vẽ được
+      - name: zh-cn-lang
+        value: 使用 Drawables
+      - name: zh-tw-lang
+        value: 使用可繪項目
     - title: Defining Custom Animations
       path: /training/material/animations.html
-      custom_link_attributes:
-      - es-lang="Definir animaciones personalizadas"
-      - in-lang="Mendefinisikan Animasi Custom"
-      - ja-lang="カスタム アニメーションの定義"
-      - ko-lang="사용자지정 애니메이션 정의"
-      - pt-br-lang="Como definir animações personalizadas"
-      - ru-lang="Определение настраиваемой анимации"
-      - vi-lang="Định nghĩa Hoạt hình Tùy chỉnh"
-      - zh-cn-lang="定义定制动画"
-      - zh-tw-lang="定義自訂動畫"
+      path_attributes:
+      - name: es-lang
+        value: Definir animaciones personalizadas
+      - name: in-lang
+        value: Mendefinisikan Animasi Custom
+      - name: ja-lang
+        value: カスタム アニメーションの定義
+      - name: ko-lang
+        value: 사용자지정 애니메이션 정의
+      - name: pt-br-lang
+        value: Como definir animações personalizadas
+      - name: ru-lang
+        value: Определение настраиваемой анимации
+      - name: vi-lang
+        value: Định nghĩa Hoạt hình Tùy chỉnh
+      - name: zh-cn-lang
+        value: 定义定制动画
+      - name: zh-tw-lang
+        value: 定義自訂動畫
     - title: Maintaining Compatibility
       path: /training/material/compatibility.html
-      custom_link_attributes:
-      - es-lang="Mantener la compatibilidad"
-      - in-lang="Mempertahankan Kompatibilitas"
-      - ja-lang="互換性の維持"
-      - ko-lang="호환성 유지"
-      - pt-br-lang="Como manter a compatibilidade"
-      - ru-lang="Обеспечение совместимости"
-      - vi-lang="Duy trì Tính tương thích"
-      - zh-cn-lang="维护兼容性"
-      - zh-tw-lang="維持相容性"
+      path_attributes:
+      - name: es-lang
+        value: Mantener la compatibilidad
+      - name: in-lang
+        value: Mempertahankan Kompatibilitas
+      - name: ja-lang
+        value: 互換性の維持
+      - name: ko-lang
+        value: 호환성 유지
+      - name: pt-br-lang
+        value: Como manter a compatibilidade
+      - name: ru-lang
+        value: Обеспечение совместимости
+      - name: vi-lang
+        value: Duy trì Tính tương thích
+      - name: zh-cn-lang
+        value: 维护兼容性
+      - name: zh-tw-lang
+        value: 維持相容性
 
 - title: Best Practices for User Input
   path: /training/best-user-input.html
   section:
   - title: Using Touch Gestures
     path: /training/gestures/index.html
-    custom_link_attributes:
-    - description="How to write apps that allow users to interact with the touch screen via touch gestures."
+    path_attributes:
+    - name: description
+      value: How to write apps that allow users to interact with the touch screen via touch gestures.
     section:
     - title: Detecting Common Gestures
       path: /training/gestures/detector.html
@@ -945,8 +1174,9 @@
       path: /training/gestures/viewgroup.html
   - title: Handling Keyboard Input
     path: /training/keyboard-input/index.html
-    custom_link_attributes:
-    - description="How to specify the appearance and behaviors of soft input methods (such as on-screen keyboards) and how to optimize the experience with hardware keyboards."
+    path_attributes:
+    - name: description
+      value: How to specify the appearance and behaviors of soft input methods (such as on-screen keyboards) and how to optimize the experience with hardware keyboards.
     section:
     - title: Specifying the Input Method Type
       path: /training/keyboard-input/style.html
@@ -958,8 +1188,9 @@
       path: /training/keyboard-input/commands.html
   - title: Supporting Game Controllers
     path: /training/game-controllers/index.html
-    custom_link_attributes:
-    - description="How to write apps that support game controllers."
+    path_attributes:
+    - name: description
+      value: How to write apps that support game controllers.
     section:
     - title: Handling Controller Actions
       path: /training/game-controllers/controller-input.html
@@ -973,8 +1204,9 @@
   section:
   - title: Running in a Background Service
     path: /training/run-background-service/index.html
-    custom_link_attributes:
-    - description="How to improve UI performance and responsiveness by sending work to a Service running in the background"
+    path_attributes:
+    - name: description
+      value: How to improve UI performance and responsiveness by sending work to a Service running in the background
     section:
     - title: Creating a Background Service
       path: /training/run-background-service/create-service.html
@@ -984,8 +1216,9 @@
       path: /training/run-background-service/report-status.html
   - title: Loading Data in the Background
     path: /training/load-data-background/index.html
-    custom_link_attributes:
-    - description="How to use CursorLoader to query data without affecting UI responsiveness."
+    path_attributes:
+    - name: description
+      value: How to use CursorLoader to query data without affecting UI responsiveness.
     section:
     - title: Running a Query with a CursorLoader
       path: /training/load-data-background/setup-loader.html
@@ -993,8 +1226,9 @@
       path: /training/load-data-background/handle-results.html
   - title: Managing Device Awake State
     path: /training/scheduling/index.html
-    custom_link_attributes:
-    - description="How to use repeating alarms and wake locks to run background jobs."
+    path_attributes:
+    - name: description
+      value: How to use repeating alarms and wake locks to run background jobs.
     section:
     - title: Keeping the Device Awake
       path: /training/scheduling/wakelock.html
@@ -1006,16 +1240,19 @@
   section:
   - title: Managing Your App's Memory
     path: /training/articles/memory.html
-    custom_link_attributes:
-    - description="How to keep your app's memory footprint small in order to improve performance on a variety of mobile devices."
+    path_attributes:
+    - name: description
+      value: How to keep your app's memory footprint small in order to improve performance on a variety of mobile devices.
   - title: Performance Tips
     path: /training/articles/perf-tips.html
-    custom_link_attributes:
-    - description="How to optimize your app's performance in various ways to improve its responsiveness and battery efficiency."
+    path_attributes:
+    - name: description
+      value: How to optimize your app's performance in various ways to improve its responsiveness and battery efficiency.
   - title: Improving Layout Performance
     path: /training/improving-layouts/index.html
-    custom_link_attributes:
-    - description="How to identify problems in your app's layout performance and improve the UI responsiveness."
+    path_attributes:
+    - name: description
+      value: How to identify problems in your app's layout performance and improve the UI responsiveness.
     section:
     - title: Optimizing Layout Hierarchies
       path: /training/improving-layouts/optimizing-layout.html
@@ -1027,11 +1264,15 @@
       path: /training/improving-layouts/smooth-scrolling.html
   - title: Optimizing Battery Life
     path: /training/monitoring-device-state/index.html
-    custom_link_attributes:
-    - es-lang="Cómo optimizar la duración de la batería"
-    - ja-lang="電池消費量の最適化"
-    - zh-cn-lang="优化电池使用时间"
-    - description="How to minimize the amount of power your app requires by adapting to current power conditions and performing power-hungry tasks at proper intervals."
+    path_attributes:
+    - name: es-lang
+      value: Cómo optimizar la duración de la batería
+    - name: ja-lang
+      value: 電池消費量の最適化
+    - name: zh-cn-lang
+      value: 优化电池使用时间
+    - name: description
+      value: How to minimize the amount of power your app requires by adapting to current power conditions and performing power-hungry tasks at proper intervals.
     section:
     - title: Reducing Network Battery Drain
       path: /training/performance/battery/network/index.html
@@ -1052,32 +1293,45 @@
       path: /training/monitoring-device-state/doze-standby.html
     - title: Monitoring the Battery Level and Charging State
       path: /training/monitoring-device-state/battery-monitoring.html
-      custom_link_attributes:
-      - es-lang="Cómo controlar el nivel de batería y el estado de carga"
-      - ja-lang="電池残量と充電状態の監視"
-      - zh-cn-lang="监控电池电量和充电状态"
+      path_attributes:
+      - name: es-lang
+        value: Cómo controlar el nivel de batería y el estado de carga
+      - name: ja-lang
+        value: 電池残量と充電状態の監視
+      - name: zh-cn-lang
+        value: 监控电池电量和充电状态
     - title: Determining and Monitoring the Docking State and Type
       path: /training/monitoring-device-state/docking-monitoring.html
-      custom_link_attributes:
-      - es-lang="Cómo determinar y controlar el tipo de conector y el estado de la conexión"
-      - ja-lang="ホルダーの装着状態とタイプの特定と監視"
-      - zh-cn-lang="确定和监控基座对接状态和类型"
+      path_attributes:
+      - name: es-lang
+        value: Cómo determinar y controlar el tipo de conector y el estado de la conexión
+      - name: ja-lang
+        value: ホルダーの装着状態とタイプの特定と監視
+      - name: zh-cn-lang
+        value: 确定和监控基座对接状态和类型
     - title: Determining and Monitoring the Connectivity Status
       path: /training/monitoring-device-state/connectivity-monitoring.html
-      custom_link_attributes:
-      - es-lang="Cómo determinar y controlar el estado de la conectividad"
-      - ja-lang="接続状態の特定と監視"
-      - zh-cn-lang="确定和监控网络连接状态"
+      path_attributes:
+      - name: es-lang
+        value: Cómo determinar y controlar el estado de la conectividad
+      - name: ja-lang
+        value: 接続状態の特定と監視
+      - name: zh-cn-lang
+        value: 确定和监控网络连接状态
     - title: Manipulating Broadcast Receivers On Demand
       path: /training/monitoring-device-state/manifest-receivers.html
-      custom_link_attributes:
-      - es-lang="Cómo manipular los receptores de emisión bajo demanda"
-      - ja-lang="オンデマンドでのブロードキャスト レシーバ操作"
-      - zh-cn-lang="根据需要操作广播接收器"
+      path_attributes:
+      - name: es-lang
+        value: Cómo manipular los receptores de emisión bajo demanda
+      - name: ja-lang
+        value: オンデマンドでのブロードキャスト レシーバ操作
+      - name: zh-cn-lang
+        value: 根据需要操作广播接收器
   - title: Sending Operations to Multiple Threads
     path: /training/multiple-threads/index.html
-    custom_link_attributes:
-    - description="How to improve the performance and scalability of long-running operations by dispatching work to multiple threads."
+    path_attributes:
+    - name: description
+      value: How to improve the performance and scalability of long-running operations by dispatching work to multiple threads.
     section:
     - title: Specifying the Code to Run on a Thread
       path: /training/multiple-threads/define-runnable.html
@@ -1089,68 +1343,81 @@
       path: /training/multiple-threads/communicate-ui.html
   - title: Keeping Your App Responsive
     path: /training/articles/perf-anr.html
-    custom_link_attributes:
-    - description="How to keep your app responsive to user interaction so the UI does not lock-up and display an \"Application Not Responding\" dialog."
+    path_attributes:
+    - name: description
+      value: How to keep your app responsive to user interaction so the UI does not lock-up and display an "Application Not Responding" dialog.
   - title: JNI Tips
     path: /training/articles/perf-jni.html
-    custom_link_attributes:
-    - description="How to efficiently use the Java Native Interface with the Android NDK."
+    path_attributes:
+    - name: description
+      value: How to efficiently use the Java Native Interface with the Android NDK.
   - title: SMP Primer for Android
     path: /training/articles/smp.html
-    custom_link_attributes:
-    - description="Tips for coding Android apps on symmetric multiprocessor systems."
+    path_attributes:
+    - name: description
+      value: Tips for coding Android apps on symmetric multiprocessor systems.
 
 - title: Best Practices for Security & Privacy
   path: /training/best-security.html
   section:
   - title: Security Tips
     path: /training/articles/security-tips.html
-    custom_link_attributes:
-    - description="How to perform various tasks and keep your app's data and your user's data secure."
+    path_attributes:
+    - name: description
+      value: How to perform various tasks and keep your app's data and your user's data secure.
   - title: Security with HTTPS and SSL
     path: /training/articles/security-ssl.html
-    custom_link_attributes:
-    - description="How to ensure that your app is secure when performing network transactions."
+    path_attributes:
+    - name: description
+      value: How to ensure that your app is secure when performing network transactions.
   - title: Updating Your Security Provider to Protect Against SSL Exploits
     path: /training/articles/security-gms-provider.html
-    custom_link_attributes:
-    - description="How to use and update Google Play services security provider, to protect against SSL exploits."
+    path_attributes:
+    - name: description
+      value: How to use and update Google Play services security provider, to protect against SSL exploits.
   - title: Checking Device Compatibility with SafetyNet
     path: /training/safetynet/index.html
-    custom_link_attributes:
-    - description="How to use the SafetyNet service to analyze a device where your app is running and get information about its compatibility with your app."
+    path_attributes:
+    - name: description
+      value: How to use the SafetyNet service to analyze a device where your app is running and get information about its compatibility with your app.
   - title: Enhancing Security with Device Management Policies
     path: /training/enterprise/device-management-policy.html
-    custom_link_attributes:
-    - description="How to create an application that enforces security policies on devices."
+    path_attributes:
+    - name: description
+      value: How to create an application that enforces security policies on devices.
 
 - title: Best Practices for Permissions & Identifiers
   path: /training/best-permissions-ids.html
   section:
   - title: Permissions and User Data
     path: /training/articles/user-data-overview.html
-    custom_link_attributes:
-    - description="Overview of app permissions on Android and how they affect your users."
+    path_attributes:
+    - name: description
+      value: Overview of app permissions on Android and how they affect your users.
   - title: Best Practices for App Permissions
     path: /training/articles/user-data-permissions.html
-    custom_link_attributes:
-    - description="How to manage permissions the right way for users."
+    path_attributes:
+    - name: description
+      value: How to manage permissions the right way for users.
   - title: Best Practices for Unique Identifiers
     path: /training/articles/user-data-ids.html
-    custom_link_attributes:
-    - description="Unique identifiers available and how to choose the right one for your use case."
+    path_attributes:
+    - name: description
+      value: Unique identifiers available and how to choose the right one for your use case.
 
 - title: Best Practices for Testing
   path: /training/testing/index.html
   section:
   - title: Getting Started with Testing
     path: /training/testing/start/index.html
-    custom_link_attributes:
-    - description="How to get started with testing your Android applications."
+    path_attributes:
+    - name: description
+      value: How to get started with testing your Android applications.
   - title: Building Effective Unit Tests
     path: /training/testing/unit-testing/index.html
-    custom_link_attributes:
-    - description="How to build effective unit tests for Android apps."
+    path_attributes:
+    - name: description
+      value: How to build effective unit tests for Android apps.
     section:
     - title: Building Local Unit Tests
       path: /training/testing/unit-testing/local-unit-tests.html
@@ -1158,8 +1425,9 @@
       path: /training/testing/unit-testing/instrumented-unit-tests.html
   - title: Automating UI Tests
     path: /training/testing/ui-testing/index.html
-    custom_link_attributes:
-    - description="How to automate your user interface tests for Android apps."
+    path_attributes:
+    - name: description
+      value: How to automate your user interface tests for Android apps.
     section:
     - title: Testing UI for a Single App
       path: /training/testing/ui-testing/espresso-testing.html
@@ -1167,8 +1435,9 @@
       path: /training/testing/ui-testing/uiautomator-testing.html
   - title: Testing App Component Integrations
     path: /training/testing/integration-testing/index.html
-    custom_link_attributes:
-    - description="How to build effective integration tests for Android apps."
+    path_attributes:
+    - name: description
+      value: How to build effective integration tests for Android apps.
     section:
     - title: Testing Your Service
       path: /training/testing/integration-testing/service-testing.html
@@ -1176,16 +1445,18 @@
       path: /training/testing/integration-testing/content-provider-testing.html
   - title: Testing Display Performance
     path: /training/testing/performance.html
-    custom_link_attributes:
-    - description="How to automate UI performance testing."
+    path_attributes:
+    - name: description
+      value: How to automate UI performance testing.
 
 - title: Using Google Play to Distribute & Monetize
   path: /training/distribute.html
   section:
   - title: Selling In-app Products
     path: /training/in-app-billing/index.html
-    custom_link_attributes:
-    - description="How to sell in-app products from your application using In-app Billing."
+    path_attributes:
+    - name: description
+      value: How to sell in-app products from your application using In-app Billing.
     section:
     - title: Preparing Your App
       path: /training/in-app-billing/preparing-iab-app.html
@@ -1197,8 +1468,9 @@
       path: /training/in-app-billing/test-iab-app.html
   - title: Maintaining Multiple APKs
     path: /training/multiple-apks/index.html
-    custom_link_attributes:
-    - description="How to publish your app on Google Play with separate APKs that target different devices, while using a single app listing."
+    path_attributes:
+    - name: description
+      value: How to publish your app on Google Play with separate APKs that target different devices, while using a single app listing.
     section:
     - title: Creating Multiple APKs for Different API Levels
       path: /training/multiple-apks/api.html
diff --git a/docs/image_sources/tools/support-library/appbar-kitkat-orig.png b/docs/image_sources/tools/support-library/appbar-kitkat-orig.png
new file mode 100644
index 0000000..ab680ce
--- /dev/null
+++ b/docs/image_sources/tools/support-library/appbar-kitkat-orig.png
Binary files differ
diff --git a/graphics/java/android/view/PixelCopy.java b/graphics/java/android/view/PixelCopy.java
index 95c930c..29bf963 100644
--- a/graphics/java/android/view/PixelCopy.java
+++ b/graphics/java/android/view/PixelCopy.java
@@ -123,6 +123,9 @@
     public static void request(@NonNull Surface source, @NonNull Bitmap dest,
             @NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread) {
         validateBitmapDest(dest);
+        if (!source.isValid()) {
+            throw new IllegalArgumentException("Surface isn't valid, source.isValid() == false");
+        }
         // TODO: Make this actually async and fast and cool and stuff
         int result = ThreadedRenderer.copySurfaceInto(source, dest);
         listenerThread.post(new Runnable() {
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index fdbe76a..0a8e3f3 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -471,18 +471,19 @@
     return *this;
 }
 
-GlopBuilder& GlopBuilder::setFillExternalTexture(Texture& texture) {
+GlopBuilder& GlopBuilder::setFillExternalTexture(Texture& texture, Matrix4& textureTransform) {
     TRIGGER_STAGE(kFillStage);
     REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
 
     mOutGlop->fill.texture = { &texture,
             GL_TEXTURE_EXTERNAL_OES, GL_LINEAR, GL_CLAMP_TO_EDGE,
-            nullptr };
+            &textureTransform };
 
     setFill(SK_ColorWHITE, 1.0f, SkXfermode::kSrc_Mode, Blend::ModeOrderSwap::NoSwap,
             nullptr, nullptr);
 
     mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
+    mDescription.hasTextureTransform = true;
     return *this;
 }
 
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index b6c186d..a9dd56f 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -73,7 +73,7 @@
     // TODO: Texture should probably know and own its target.
     // setFillLayer() forces it to GL_TEXTURE which isn't always correct.
     // Similarly setFillLayer normally forces its own wrap & filter mode
-    GlopBuilder& setFillExternalTexture(Texture& texture);
+    GlopBuilder& setFillExternalTexture(Texture& texture, Matrix4& textureTransform);
 
     GlopBuilder& setTransform(const Snapshot& snapshot, const int transformFlags) {
         return setTransform(*snapshot.transform, transformFlags);
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 36007cd..9cde5d6 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -236,6 +236,8 @@
 
     static const Matrix4& identity();
 
+    void invalidateType() { mType = kTypeUnknown; }
+
 private:
     mutable uint8_t mType;
 
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 49596e1..55f823d 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -101,7 +101,10 @@
     // Setup the source
     sp<GraphicBuffer> sourceBuffer;
     sp<Fence> sourceFence;
-    status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence);
+    Matrix4 texTransform;
+    status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence,
+            texTransform.data);
+    texTransform.invalidateType();
     if (err != NO_ERROR) {
         ALOGW("Failed to get last queued buffer, error = %d", err);
         return CopyResult::UnknownError;
@@ -163,8 +166,8 @@
         Glop glop;
         GlopBuilder(renderState, caches, &glop)
                 .setRoundRectClipState(nullptr)
-                .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
-                .setFillExternalTexture(sourceTexture)
+                .setMeshTexturedUnitQuad(nullptr)
+                .setFillExternalTexture(sourceTexture, texTransform)
                 .setTransform(Matrix4::identity(), TransformFlags::None)
                 .setModelViewMapUnitToRect(destRect)
                 .build();
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 05e12a1..a8c2652 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -2688,20 +2688,20 @@
         public static final int VP9Profile3HDR = 0x2000;
 
         // from OMX_VIDEO_VP9LEVELTYPE
-        public static final int VP9Level1  = 0x0;
-        public static final int VP9Level11 = 0x1;
-        public static final int VP9Level2  = 0x2;
-        public static final int VP9Level21 = 0x4;
-        public static final int VP9Level3  = 0x8;
-        public static final int VP9Level31 = 0x10;
-        public static final int VP9Level4  = 0x20;
-        public static final int VP9Level41 = 0x40;
-        public static final int VP9Level5  = 0x80;
-        public static final int VP9Level51 = 0x100;
-        public static final int VP9Level52 = 0x200;
-        public static final int VP9Level6  = 0x400;
-        public static final int VP9Level61 = 0x800;
-        public static final int VP9Level62 = 0x1000;
+        public static final int VP9Level1  = 0x1;
+        public static final int VP9Level11 = 0x2;
+        public static final int VP9Level2  = 0x4;
+        public static final int VP9Level21 = 0x8;
+        public static final int VP9Level3  = 0x10;
+        public static final int VP9Level31 = 0x20;
+        public static final int VP9Level4  = 0x40;
+        public static final int VP9Level41 = 0x80;
+        public static final int VP9Level5  = 0x100;
+        public static final int VP9Level51 = 0x200;
+        public static final int VP9Level52 = 0x400;
+        public static final int VP9Level6  = 0x800;
+        public static final int VP9Level61 = 0x1000;
+        public static final int VP9Level62 = 0x2000;
 
         // from OMX_VIDEO_HEVCPROFILETYPE
         public static final int HEVCProfileMain        = 0x01;
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 1d894e1..22bb5f1 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -188,6 +188,9 @@
             RECORDING_ERROR_RESOURCE_BUSY})
     public @interface RecordingError {}
 
+    static final int RECORDING_ERROR_START = 0;
+    static final int RECORDING_ERROR_END = 2;
+
     /**
      * Error for {@link TvInputService.RecordingSession#notifyError(int)} and
      * {@link TvRecordingClient.RecordingCallback#onError(int)}: The requested operation cannot be
@@ -195,7 +198,7 @@
      * for the problem is defined on the higher version than application's
      * <code>android:targetSdkVersion</code>.
      */
-    public static final int RECORDING_ERROR_UNKNOWN = 0;
+    public static final int RECORDING_ERROR_UNKNOWN = RECORDING_ERROR_START;
 
     /**
      * Error for {@link TvInputService.RecordingSession#notifyError(int)} and
@@ -209,7 +212,7 @@
      * {@link TvRecordingClient.RecordingCallback#onError(int)}: Recording cannot proceed because
      * a required recording resource was not able to be allocated.
      */
-    public static final int RECORDING_ERROR_RESOURCE_BUSY = 2;
+    public static final int RECORDING_ERROR_RESOURCE_BUSY = RECORDING_ERROR_END;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 488b284..168fcdc 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1613,7 +1613,14 @@
          * <li>{@link TvInputManager#RECORDING_ERROR_RESOURCE_BUSY}
          * </ul>
          */
-        public void notifyError(@TvInputManager.RecordingError final int error) {
+        public void notifyError(@TvInputManager.RecordingError int error) {
+            if (error < TvInputManager.RECORDING_ERROR_START
+                    || error > TvInputManager.RECORDING_ERROR_END) {
+                Log.w(TAG, "notifyError - invalid error code (" + error
+                        + ") is changed to RECORDING_ERROR_UNKNOWN.");
+                error = TvInputManager.RECORDING_ERROR_UNKNOWN;
+            }
+            final int validError = error;
             executeOrPostRunnableOnMainThread(new Runnable() {
                 @MainThread
                 @Override
@@ -1621,7 +1628,7 @@
                     try {
                         if (DEBUG) Log.d(TAG, "notifyError");
                         if (mSessionCallback != null) {
-                            mSessionCallback.onError(error);
+                            mSessionCallback.onError(validError);
                         }
                     } catch (RemoteException e) {
                         Log.w(TAG, "error in notifyError", e);
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 74c98e4..efa34ff 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -421,8 +421,6 @@
     <string name="wifi_aggressive_handover">Aggressive Wi\u2011Fi to Cellular handover</string>
     <!-- Setting Checkbox title whether to enable WiFi Scanning in the presence of traffic. [CHAR LIMIT=80] -->
     <string name="wifi_allow_scan_with_traffic">Always allow Wi\u2011Fi Roam Scans</string>
-    <!-- Setting Checkbox title whether to enable WiFi Scanning in the presence of traffic. [CHAR LIMIT=80] -->
-    <string name="legacy_dhcp_client">Use legacy DHCP client</string>
     <!-- Setting Checkbox title whether to always keep cellular data active. [CHAR LIMIT=80] -->
     <string name="mobile_data_always_on">Cellular data always active</string>
     <!-- Setting Checkbox title for disabling Bluetooth absolute volume -->
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index c72f5d2..0962d84 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -158,6 +158,7 @@
         private int mLastRequestedHeight = -1;
         private AsyncTask<Void, Void, Bitmap> mLoader;
         private boolean mNeedsDrawAfterLoadingWallpaper;
+        private boolean mSurfaceValid;
 
         public DrawableEngine() {
             super();
@@ -249,11 +250,6 @@
         }
 
         @Override
-        public void onTouchEvent(MotionEvent event) {
-            super.onTouchEvent(event);
-        }
-
-        @Override
         public void onOffsetsChanged(float xOffset, float yOffset,
                 float xOffsetStep, float yOffsetStep,
                 int xPixels, int yPixels) {
@@ -288,13 +284,23 @@
         @Override
         public void onSurfaceDestroyed(SurfaceHolder holder) {
             super.onSurfaceDestroyed(holder);
+            if (DEBUG) {
+                Log.i(TAG, "onSurfaceDestroyed");
+            }
+
             mLastSurfaceWidth = mLastSurfaceHeight = -1;
+            mSurfaceValid = false;
         }
 
         @Override
         public void onSurfaceCreated(SurfaceHolder holder) {
             super.onSurfaceCreated(holder);
+            if (DEBUG) {
+                Log.i(TAG, "onSurfaceCreated");
+            }
+
             mLastSurfaceWidth = mLastSurfaceHeight = -1;
+            mSurfaceValid = true;
         }
 
         @Override
@@ -314,6 +320,9 @@
         }
 
         void drawFrame() {
+            if (!mSurfaceValid) {
+                return;
+            }
             try {
                 DisplayInfo displayInfo = getDefaultDisplayInfo();
                 int newRotation = displayInfo.rotation;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 23e3561..f7ecd61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1985,19 +1985,29 @@
                 artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), artworkBitmap);
             }
         }
+        boolean allowWhenShade = false;
         if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) {
             Bitmap lockWallpaper = mLockscreenWallpaper.getBitmap();
             if (lockWallpaper != null) {
                 artworkDrawable = new LockscreenWallpaper.WallpaperDrawable(
                         mBackdropBack.getResources(), lockWallpaper);
+                // We're in the SHADE mode on the SIM screen - yet we still need to show
+                // the lockscreen wallpaper in that mode.
+                allowWhenShade = mStatusBarKeyguardViewManager != null
+                        && mStatusBarKeyguardViewManager.isShowing();
             }
         }
 
+        boolean hideBecauseOccluded = mStatusBarKeyguardViewManager != null
+                && mStatusBarKeyguardViewManager.isOccluded();
+
         final boolean hasArtwork = artworkDrawable != null;
 
-        if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK) && mState != StatusBarState.SHADE
+        if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
+                && (mState != StatusBarState.SHADE || allowWhenShade)
                 && mFingerprintUnlockController.getMode()
-                        != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) {
+                        != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+                && !hideBecauseOccluded) {
             // time to show some art!
             if (mBackdrop.getVisibility() != View.VISIBLE) {
                 mBackdrop.setVisibility(View.VISIBLE);
@@ -2065,7 +2075,8 @@
                     Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
                 }
                 if (mFingerprintUnlockController.getMode()
-                        == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) {
+                        == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+                        || hideBecauseOccluded) {
 
                     // We are unlocking directly - no animation!
                     mBackdrop.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 117e2b3..dcad75a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -247,6 +247,7 @@
             }
         }
         mOccluded = occluded;
+        mPhoneStatusBar.updateMediaMetaData(false, false);
         mStatusBarWindowManager.setKeyguardOccluded(occluded);
         reset();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index c82ba3b..6800772 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -119,7 +119,7 @@
         filter.addAction(Intent.ACTION_USER_REMOVED);
         filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
         filter.addAction(Intent.ACTION_USER_SWITCHED);
-        filter.addAction(Intent.ACTION_USER_STOPPING);
+        filter.addAction(Intent.ACTION_USER_STOPPED);
         filter.addAction(Intent.ACTION_USER_UNLOCKED);
         mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
                 null /* permission */, null /* scheduler */);
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
index dd12360..fe5d8bc 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
@@ -134,7 +134,8 @@
      */
     public void requestFocus(boolean allowRecentsFocusable) {
         mRecentsView.setVisibility(allowRecentsFocusable ? View.VISIBLE : View.GONE);
-        if (!mIsPipRecentsOverlayShown || !mIsRecentsShown || mIsPipFocusedInRecent) {
+        if (!mIsPipRecentsOverlayShown || !mIsRecentsShown || mIsPipFocusedInRecent
+                || !mPipManager.isPipShown()) {
             return;
         }
         mIsPipFocusedInRecent = true;
@@ -153,7 +154,8 @@
      * This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
      */
     public void clearFocus() {
-        if (!mIsPipRecentsOverlayShown || !mIsRecentsShown || !mIsPipFocusedInRecent) {
+        if (!mIsPipRecentsOverlayShown || !mIsRecentsShown || !mIsPipFocusedInRecent
+                || !mPipManager.isPipShown()) {
             return;
         }
         if (!mRecentsView.hasFocus()) {
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index f49235c..c417fe8 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2187,6 +2187,10 @@
     // reinflation.
     ACTION_REMOTE_INPUT_CLOSE = 400;
 
+    // OPEN: Settings > Accounts > Work profile settings
+    // CATEGORY: SETTINGS
+    ACCOUNTS_WORK_PROFILE_SETTINGS = 401;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index fc2e95d..ae31438 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1245,8 +1245,8 @@
     private void updateServicesLocked(UserState userState) {
         Map<ComponentName, Service> componentNameToServiceMap =
                 userState.mComponentNameToServiceMap;
-        boolean isUnlocked = mContext.getSystemService(UserManager.class)
-                .isUserUnlocked(userState.mUserId);
+        boolean isUnlockingOrUnlocked = mContext.getSystemService(UserManager.class)
+                .isUserUnlockingOrUnlocked(userState.mUserId);
 
         for (int i = 0, count = userState.mInstalledServices.size(); i < count; i++) {
             AccessibilityServiceInfo installedService = userState.mInstalledServices.get(i);
@@ -1256,7 +1256,7 @@
             Service service = componentNameToServiceMap.get(componentName);
 
             // Ignore non-encryption-aware services until user is unlocked
-            if (!isUnlocked && !installedService.isDirectBootAware()) {
+            if (!isUnlockingOrUnlocked && !installedService.isDirectBootAware()) {
                 Slog.d(LOG_TAG, "Ignoring non-encryption-aware service " + componentName);
                 continue;
             }
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 0428ecf..7b9d4456 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -335,7 +335,7 @@
                     Provider provider = installedProviders.get(i);
 
                     final int userId = provider.getUserId();
-                    if (!mUserManager.isUserUnlocked(userId) ||
+                    if (!mUserManager.isUserUnlockingOrUnlocked(userId) ||
                             isProfileWithLockedParent(userId)) {
                         continue;
                     }
@@ -369,7 +369,7 @@
     }
 
     private void onPackageBroadcastReceived(Intent intent, int userId) {
-        if (!mUserManager.isUserUnlocked(userId) ||
+        if (!mUserManager.isUserUnlockingOrUnlocked(userId) ||
                 isProfileWithLockedParent(userId)) {
             return;
         }
@@ -455,7 +455,7 @@
      * userId must be the group parent.
      */
     private void reloadWidgetsMaskedStateForGroup(int userId) {
-        if (!mUserManager.isUserUnlocked(userId)) {
+        if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
             return;
         }
         synchronized (mLock) {
@@ -472,7 +472,7 @@
         try {
             UserInfo user  = mUserManager.getUserInfo(userId);
 
-            boolean lockedProfile = !mUserManager.isUserUnlocked(userId);
+            boolean lockedProfile = !mUserManager.isUserUnlockingOrUnlocked(userId);
             boolean quietProfile = user.isQuietModeEnabled();
             final int N = mProviders.size();
             for (int i = 0; i < N; i++) {
@@ -656,7 +656,7 @@
     }
 
     private void ensureGroupStateLoadedLocked(int userId) {
-        if (!mUserManager.isUserUnlocked(userId)) {
+        if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
             throw new IllegalStateException(
                     "User " + userId + " must be unlocked for widgets to be available");
         }
@@ -3363,7 +3363,7 @@
             if (userInfo != null && userInfo.isManagedProfile()) {
                 UserInfo parentInfo = mUserManager.getProfileParent(userId);
                 if (parentInfo != null
-                        && !mUserManager.isUserUnlocked(parentInfo.getUserHandle())) {
+                        && !mUserManager.isUserUnlockingOrUnlocked(parentInfo.getUserHandle())) {
                     return true;
                 }
             }
@@ -3378,7 +3378,7 @@
         if (userInfo != null && userInfo.isManagedProfile()) {
             UserInfo parentInfo = mUserManager.getProfileParent(userId);
             if (parentInfo != null
-                    && mUserManager.isUserUnlocked(parentInfo.getUserHandle())) {
+                    && mUserManager.isUserUnlockingOrUnlocked(parentInfo.getUserHandle())) {
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0287332..d85827e 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -730,7 +730,7 @@
         //set up the listener for user state for creating user VPNs
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_USER_STARTING);
-        intentFilter.addAction(Intent.ACTION_USER_STOPPING);
+        intentFilter.addAction(Intent.ACTION_USER_STOPPED);
         intentFilter.addAction(Intent.ACTION_USER_ADDED);
         intentFilter.addAction(Intent.ACTION_USER_REMOVED);
         intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
@@ -3619,7 +3619,7 @@
         synchronized(mVpns) {
             Vpn userVpn = mVpns.get(userId);
             if (userVpn == null) {
-                loge("Stopping user has no VPN");
+                loge("Stopped user has no VPN");
                 return;
             }
             mVpns.delete(userId);
@@ -3664,7 +3664,7 @@
 
             if (Intent.ACTION_USER_STARTING.equals(action)) {
                 onUserStart(userId);
-            } else if (Intent.ACTION_USER_STOPPING.equals(action)) {
+            } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
                 onUserStop(userId);
             } else if (Intent.ACTION_USER_ADDED.equals(action)) {
                 onUserAdded(userId);
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 79d16da..a584f70 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -1020,7 +1020,7 @@
         // If the system is not ready or the device is not yed unlocked by the user, then we use
         // copy-on-write settings.
         final boolean useCopyOnWriteSettings =
-                !mSystemReady || !mUserManager.isUserUnlocked(newUserId);
+                !mSystemReady || !mUserManager.isUserUnlockingOrUnlocked(newUserId);
         mSettings.switchCurrentUser(newUserId, useCopyOnWriteSettings);
         updateCurrentProfileIds();
         // InputMethodFileManager should be reset when the user is changed
@@ -1077,7 +1077,7 @@
                 mSystemReady = true;
                 final int currentUserId = mSettings.getCurrentUserId();
                 mSettings.switchCurrentUser(currentUserId,
-                        !mUserManager.isUserUnlocked(currentUserId));
+                        !mUserManager.isUserUnlockingOrUnlocked(currentUserId));
                 mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
                 mNotificationManager = mContext.getSystemService(NotificationManager.class);
                 mStatusBar = statusBar;
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index a3ef6b6..cbf7e80 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -256,13 +256,13 @@
         for (int i = 0; i < users.size(); i++) {
             UserInfo user = users.get(i);
             UserHandle userHandle = user.getUserHandle();
-            if (!mUserManager.isUserUnlocked(userHandle)) {
+            if (!mUserManager.isUserUnlockingOrUnlocked(userHandle)) {
                 if (!user.isManagedProfile()) {
                     showEncryptionNotification(userHandle);
                 } else {
                     UserInfo parent = mUserManager.getProfileParent(user.id);
                     if (parent != null &&
-                            mUserManager.isUserUnlocked(parent.getUserHandle()) &&
+                            mUserManager.isUserUnlockingOrUnlocked(parent.getUserHandle()) &&
                             !mUserManager.isQuietModeEnabled(userHandle)) {
                         // Only show notifications for managed profiles once their parent
                         // user is unlocked.
@@ -350,7 +350,7 @@
             UserInfo profile = profiles.get(i);
             if (profile.isManagedProfile()) {
                 UserHandle userHandle = profile.getUserHandle();
-                if (!mUserManager.isUserUnlocked(userHandle) &&
+                if (!mUserManager.isUserUnlockingOrUnlocked(userHandle) &&
                         !mUserManager.isQuietModeEnabled(userHandle)) {
                     showEncryptionNotificationForProfile(userHandle);
                 }
@@ -1281,8 +1281,14 @@
         // service can't connect to vold, it restarts, and then the new instance
         // does successfully connect.
         final IMountService service = getMountService();
-        String password = service.getPassword();
-        service.clearPassword();
+        String password;
+        long identity = Binder.clearCallingIdentity();
+        try {
+            password = service.getPassword();
+            service.clearPassword();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
         if (password == null) {
             return false;
         }
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index ec05dae..7253870 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2648,6 +2648,8 @@
      */
     @Override
     public int getPasswordType() {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
+            "no permission to access the crypt keeper");
 
         waitForReady();
 
@@ -2672,6 +2674,8 @@
      */
     @Override
     public void setField(String field, String contents) throws RemoteException {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
+            "no permission to access the crypt keeper");
 
         waitForReady();
 
@@ -2690,6 +2694,8 @@
      */
     @Override
     public String getField(String field) throws RemoteException {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
+            "no permission to access the crypt keeper");
 
         waitForReady();
 
@@ -2714,6 +2720,8 @@
      */
     @Override
     public boolean isConvertibleToFBE() throws RemoteException {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
+            "no permission to access the crypt keeper");
 
         waitForReady();
 
@@ -2728,8 +2736,9 @@
 
     @Override
     public String getPassword() throws RemoteException {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE,
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
                 "only keyguard can retrieve password");
+
         if (!isReady()) {
             return new String();
         }
@@ -2752,6 +2761,9 @@
 
     @Override
     public void clearPassword() throws RemoteException {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
+                "only keyguard can clear password");
+
         if (!isReady()) {
             return;
         }
@@ -2816,17 +2828,19 @@
         enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
         waitForReady();
 
-        // When a user has secure lock screen, require a challenge token to
-        // actually unlock. This check is mostly in place for emulation mode.
-        if (mLockPatternUtils.isSecure(userId) && ArrayUtils.isEmpty(token)) {
-            throw new IllegalStateException("Token required to unlock secure user " + userId);
-        }
+        if (StorageManager.isFileEncryptedNativeOrEmulated()) {
+            // When a user has secure lock screen, require a challenge token to
+            // actually unlock. This check is mostly in place for emulation mode.
+            if (mLockPatternUtils.isSecure(userId) && ArrayUtils.isEmpty(token)) {
+                throw new IllegalStateException("Token required to unlock secure user " + userId);
+            }
 
-        try {
-            mCryptConnector.execute("cryptfs", "unlock_user_key", userId, serialNumber,
-                    encodeBytes(token), encodeBytes(secret));
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            try {
+                mCryptConnector.execute("cryptfs", "unlock_user_key", userId, serialNumber,
+                        encodeBytes(token), encodeBytes(secret));
+            } catch (NativeDaemonConnectorException e) {
+                throw e.rethrowAsParcelableException();
+            }
         }
 
         synchronized (mLock) {
@@ -2852,12 +2866,8 @@
 
     @Override
     public boolean isUserKeyUnlocked(int userId) {
-        if (StorageManager.isFileEncryptedNativeOrEmulated()) {
-            synchronized (mLock) {
-                return ArrayUtils.contains(mLocalUnlockedUsers, userId);
-            }
-        } else {
-            return true;
+        synchronized (mLock) {
+            return ArrayUtils.contains(mLocalUnlockedUsers, userId);
         }
     }
 
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index a628747..4b0d4be 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -166,7 +166,7 @@
         mMonitor = new TextServicesMonitor();
         mMonitor.register(context, null, true);
         final boolean useCopyOnWriteSettings =
-                !mSystemReady || !mUserManager.isUserUnlocked(userId);
+                !mSystemReady || !mUserManager.isUserUnlockingOrUnlocked(userId);
         mSettings = new TextServicesSettings(context.getContentResolver(), userId,
                 useCopyOnWriteSettings);
 
@@ -176,7 +176,7 @@
 
     private void resetInternalState(@UserIdInt int userId) {
         final boolean useCopyOnWriteSettings =
-                !mSystemReady || !mUserManager.isUserUnlocked(userId);
+                !mSystemReady || !mUserManager.isUserUnlockingOrUnlocked(userId);
         mSettings.switchCurrentUser(userId, useCopyOnWriteSettings);
         updateCurrentProfileIds();
         unbindServiceLocked();
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 480da72..96214d6 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -304,7 +304,7 @@
     }
 
     private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
-    private final SparseBooleanArray mUnlockedUsers = new SparseBooleanArray();
+    private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray();
 
     private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
     private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
@@ -424,7 +424,7 @@
         if (Log.isLoggable(TAG, Log.DEBUG)) {
             Log.d(TAG, "validateAccountsInternal " + accounts.userId
                     + " isCeDatabaseAttached=" + accounts.openHelper.isCeDatabaseAttached()
-                    + " userLocked=" + mUnlockedUsers.get(accounts.userId));
+                    + " userLocked=" + mLocalUnlockedUsers.get(accounts.userId));
         }
         if (invalidateAuthenticatorCache) {
             mAuthenticatorCache.invalidateCache(accounts.userId);
@@ -575,7 +575,7 @@
                 validateAccounts = true;
             }
             // open CE database if necessary
-            if (!accounts.openHelper.isCeDatabaseAttached() && mUnlockedUsers.get(userId)) {
+            if (!accounts.openHelper.isCeDatabaseAttached() && mLocalUnlockedUsers.get(userId)) {
                 Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
                 synchronized (accounts.cacheLock) {
                     File preNDatabaseFile = new File(getPreNDatabaseName(userId));
@@ -648,8 +648,8 @@
         synchronized (mUsers) {
             accounts = mUsers.get(userId);
             mUsers.remove(userId);
-            userUnlocked = mUnlockedUsers.get(userId);
-            mUnlockedUsers.delete(userId);
+            userUnlocked = mLocalUnlockedUsers.get(userId);
+            mLocalUnlockedUsers.delete(userId);
         }
         if (accounts != null) {
             synchronized (accounts.cacheLock) {
@@ -686,7 +686,7 @@
             Log.v(TAG, "onUserUnlocked " + userId);
         }
         synchronized (mUsers) {
-            mUnlockedUsers.put(userId, true);
+            mLocalUnlockedUsers.put(userId, true);
         }
         if (userId < 1) return;
         syncSharedAccounts(userId);
@@ -746,7 +746,7 @@
         if (account == null) {
             return null;
         }
-        if (!isUserUnlocked(accounts.userId)) {
+        if (!isLocalUnlockedUser(accounts.userId)) {
             Log.w(TAG, "Password is not available - user " + accounts.userId + " data is locked");
             return null;
         }
@@ -828,7 +828,7 @@
                     account.type);
             throw new SecurityException(msg);
         }
-        if (!isUserUnlocked(userId)) {
+        if (!isLocalUnlockedUser(userId)) {
             Log.w(TAG, "User " + userId + " data is locked. callingUid " + callingUid);
             return null;
         }
@@ -1109,7 +1109,7 @@
         if (account == null) {
             return false;
         }
-        if (!isUserUnlocked(accounts.userId)) {
+        if (!isLocalUnlockedUser(accounts.userId)) {
             Log.w(TAG, "Account " + account + " cannot be added - user " + accounts.userId
                     + " is locked. callingUid=" + callingUid);
             return false;
@@ -1176,9 +1176,9 @@
         return true;
     }
 
-    private boolean isUserUnlocked(int userId) {
+    private boolean isLocalUnlockedUser(int userId) {
         synchronized (mUsers) {
-            return mUnlockedUsers.get(userId);
+            return mLocalUnlockedUsers.get(userId);
         }
     }
 
@@ -1193,7 +1193,7 @@
         for (UserInfo user : users) {
             if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
                 addSharedAccountAsUser(account, user.id);
-                if (getUserManager().isUserUnlocked(user.id)) {
+                if (isLocalUnlockedUser(user.id)) {
                     mMessageHandler.sendMessage(mMessageHandler.obtainMessage(
                             MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
                 }
@@ -1597,7 +1597,7 @@
 
     private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
         int deleted;
-        boolean userUnlocked = isUserUnlocked(accounts.userId);
+        boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
         if (!userUnlocked) {
             Slog.i(TAG, "Removing account " + account + " while user "+ accounts.userId
                     + " is still locked. CE data will be removed later");
@@ -1792,7 +1792,7 @@
                     account.type);
             throw new SecurityException(msg);
         }
-        if (!isUserUnlocked(userId)) {
+        if (!isLocalUnlockedUser(userId)) {
             Log.w(TAG, "Authtoken not available - user " + userId + " data is locked. callingUid "
                     + callingUid);
             return null;
@@ -4037,8 +4037,7 @@
                 return false;
             }
 
-            final ActivityManager am = mContext.getSystemService(ActivityManager.class);
-            if (am.isUserRunningAndLocked(mAccounts.userId)
+            if (!isLocalUnlockedUser(mAccounts.userId)
                     && !authenticatorInfo.componentInfo.directBootAware) {
                 Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
                         + " which isn't encryption aware");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9d7ddc7..ddb9b8a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1946,9 +1946,7 @@
                     startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
                 }
                 installEncryptionUnawareProviders(userId);
-                if (msg.obj instanceof ProgressReporter) {
-                    ((ProgressReporter) msg.obj).finish();
-                }
+                mUserController.finishUserUnlocked((UserState) msg.obj);
                 break;
             }
             case SYSTEM_USER_CURRENT_MSG: {
@@ -5949,6 +5947,15 @@
 
     }
 
+    final boolean clearBroadcastQueueForUserLocked(int userId) {
+        boolean didSomething = false;
+        for (int i = mBroadcastQueues.length - 1; i >= 0; i--) {
+            didSomething |= mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(
+                    null, null, userId, true);
+        }
+        return didSomething;
+    }
+
     final boolean forceStopPackageLocked(String packageName, int appId,
             boolean callerWillRestart, boolean purgeCache, boolean doit,
             boolean evenPersistent, boolean uninstalling, int userId, String reason) {
@@ -6304,7 +6311,11 @@
         app.debugging = false;
         app.cached = false;
         app.killedByAm = false;
-        app.unlocked = mContext.getSystemService(UserManager.class).isUserUnlocked(app.userId);
+
+        // We carefully use the same state that PackageManager uses for
+        // filtering, since we use this flag to decide if we need to install
+        // providers when user is unlocked later
+        app.unlocked = StorageManager.isUserKeyUnlocked(app.userId);
 
         mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
 
@@ -11024,14 +11035,6 @@
      * belonging to any running apps.
      */
     private void installEncryptionUnawareProviders(int userId) {
-        if (!StorageManager.isFileEncryptedNativeOrEmulated()) {
-            // TODO: eventually pivot this back to look at current user state,
-            // similar to the comment in UserManager.isUserUnlocked(), but for
-            // now, if we started apps when "unlocked" then unaware providers
-            // have already been spun up.
-            return;
-        }
-
         // We're only interested in providers that are encryption unaware, and
         // we don't care about uninstalled apps, since there's no way they're
         // running at this point.
@@ -17178,6 +17181,7 @@
             if (isProtectedBroadcast
                     || Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
                     || Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(action)
+                    || Intent.ACTION_MEDIA_BUTTON.equals(action)
                     || Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)
                     || Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(action)
                     || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 94e442f..33b87b6 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -685,8 +685,8 @@
                     Binder.restoreCallingIdentity(token);
                 }
                 if (parent != null
-                        && userManager.isUserUnlocked(parent.getUserHandle())
-                        && !userManager.isUserUnlocked(userInfo.getUserHandle())) {
+                        && userManager.isUserUnlockingOrUnlocked(parent.getUserHandle())
+                        && !userManager.isUserUnlockingOrUnlocked(userInfo.getUserHandle())) {
                     rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
                             PackageManager.MATCH_DIRECT_BOOT_AWARE
                                     | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index cdb68d8..bfa5b42 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -270,7 +270,7 @@
             if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
 
             // Only keep marching forward if user is actually unlocked
-            if (!isUserKeyUnlocked(userId)) return;
+            if (!StorageManager.isUserKeyUnlocked(userId)) return;
 
             if (uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) {
                 uss.mUnlockProgress.start();
@@ -281,9 +281,29 @@
                 mUserManager.onBeforeUnlockUser(userId);
                 uss.mUnlockProgress.setProgress(20);
 
-                // Dispatch unlocked to system services
-                mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss.mUnlockProgress)
+                // Dispatch unlocked to system services; when fully dispatched,
+                // that calls through to the next "unlocked" phase
+                mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
                         .sendToTarget();
+            }
+        }
+    }
+
+    /**
+     * Step from {@link UserState#STATE_RUNNING_UNLOCKING} to
+     * {@link UserState#STATE_RUNNING_UNLOCKED}.
+     */
+    void finishUserUnlocked(final UserState uss) {
+        final int userId = uss.mHandle.getIdentifier();
+        synchronized (mService) {
+            // Bail if we ended up with a stale user
+            if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
+
+            // Only keep marching forward if user is actually unlocked
+            if (!StorageManager.isUserKeyUnlocked(userId)) return;
+
+            if (uss.setState(STATE_RUNNING_UNLOCKING, STATE_RUNNING_UNLOCKED)) {
+                uss.mUnlockProgress.finish();
 
                 // Dispatch unlocked to external apps
                 final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
@@ -310,27 +330,25 @@
                     }
                 }
 
-                // Send PRE_BOOT broadcasts if fingerprint changed
+                // Send PRE_BOOT broadcasts if user fingerprint changed; we
+                // purposefully block sending BOOT_COMPLETED until after all
+                // PRE_BOOT receivers are finished to avoid ANR'ing apps
                 final UserInfo info = getUserInfo(userId);
                 if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)) {
                     new PreBootBroadcaster(mService, userId, null) {
                         @Override
                         public void onFinished() {
-                            finishUserUnlocked(uss);
+                            finishUserUnlockedCompleted(uss);
                         }
                     }.sendNext();
                 } else {
-                    finishUserUnlocked(uss);
+                    finishUserUnlockedCompleted(uss);
                 }
             }
         }
     }
 
-    /**
-     * Step from {@link UserState#STATE_RUNNING_UNLOCKING} to
-     * {@link UserState#STATE_RUNNING_UNLOCKED}.
-     */
-    private void finishUserUnlocked(UserState uss) {
+    private void finishUserUnlockedCompleted(UserState uss) {
         final int userId = uss.mHandle.getIdentifier();
         synchronized (mService) {
             // Bail if we ended up with a stale user
@@ -341,39 +359,38 @@
             }
 
             // Only keep marching forward if user is actually unlocked
-            if (!isUserKeyUnlocked(userId)) return;
+            if (!StorageManager.isUserKeyUnlocked(userId)) return;
 
-            if (uss.setState(STATE_RUNNING_UNLOCKING, STATE_RUNNING_UNLOCKED)) {
-                // Remember that we logged in
-                mUserManager.onUserLoggedIn(userId);
+            // Remember that we logged in
+            mUserManager.onUserLoggedIn(userId);
 
-                if (!userInfo.isInitialized()) {
-                    if (userId != UserHandle.USER_SYSTEM) {
-                        Slog.d(TAG, "Initializing user #" + userId);
-                        Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
-                        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-                        mService.broadcastIntentLocked(null, null, intent, null,
-                                new IIntentReceiver.Stub() {
-                                    @Override
-                                    public void performReceive(Intent intent, int resultCode,
-                                            String data, Bundle extras, boolean ordered,
-                                            boolean sticky, int sendingUser) {
-                                        // Note: performReceive is called with mService lock held
-                                        getUserManager().makeInitialized(userInfo.id);
-                                    }
-                                }, 0, null, null, null, AppOpsManager.OP_NONE,
-                                null, true, false, MY_PID, SYSTEM_UID, userId);
-                    }
+            if (!userInfo.isInitialized()) {
+                if (userId != UserHandle.USER_SYSTEM) {
+                    Slog.d(TAG, "Initializing user #" + userId);
+                    Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
+                    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                    mService.broadcastIntentLocked(null, null, intent, null,
+                            new IIntentReceiver.Stub() {
+                                @Override
+                                public void performReceive(Intent intent, int resultCode,
+                                        String data, Bundle extras, boolean ordered,
+                                        boolean sticky, int sendingUser) {
+                                    // Note: performReceive is called with mService lock held
+                                    getUserManager().makeInitialized(userInfo.id);
+                                }
+                            }, 0, null, null, null, AppOpsManager.OP_NONE,
+                            null, true, false, MY_PID, SYSTEM_UID, userId);
                 }
-                Slog.d(TAG, "Sending BOOT_COMPLETE user #" + userId);
-                final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
-                bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-                bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
-                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-                mService.broadcastIntentLocked(null, null, bootIntent, null, null, 0, null, null,
-                        new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED },
-                        AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
             }
+
+            Slog.d(TAG, "Sending BOOT_COMPLETE user #" + userId);
+            final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
+            bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+            bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
+                    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+            mService.broadcastIntentLocked(null, null, bootIntent, null, null, 0, null, null,
+                    new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED },
+                    AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
         }
     }
 
@@ -483,6 +500,8 @@
                         });
                     }
                 };
+                // Clear broadcast queue for the user to avoid delivering stale broadcasts
+                mService.clearBroadcastQueueForUserLocked(userId);
                 // Kick things off.
                 mService.broadcastIntentLocked(null, null, stoppingIntent,
                         null, stoppingReceiver, 0, null, null,
@@ -680,20 +699,6 @@
         return IMountService.Stub.asInterface(ServiceManager.getService("mount"));
     }
 
-    private boolean isUserKeyUnlocked(int userId) {
-        final IMountService mountService = getMountService();
-        if (mountService != null) {
-            try {
-                return mountService.isUserKeyUnlocked(userId);
-            } catch (RemoteException e) {
-                throw e.rethrowAsRuntimeException();
-            }
-        } else {
-            Slog.w(TAG, "Mount service not published; guessing locked state based on property");
-            return !StorageManager.isFileEncryptedNativeOrEmulated();
-        }
-    }
-
     /**
      * Start user, if its not already running.
      * <p>The user will be brought to the foreground, if {@code foreground} parameter is set.
@@ -935,7 +940,7 @@
             }
         }
 
-        if (!isUserKeyUnlocked(userId)) {
+        if (!StorageManager.isUserKeyUnlocked(userId)) {
             final UserInfo userInfo = getUserInfo(userId);
             final IMountService mountService = getMountService();
             try {
@@ -1321,30 +1326,31 @@
         if ((flags & ActivityManager.FLAG_OR_STOPPED) != 0) {
             return true;
         }
-
-        final boolean unlocked;
-        switch (state.state) {
-            case UserState.STATE_STOPPING:
-            case UserState.STATE_SHUTDOWN:
-            default:
-                return false;
-
-            case UserState.STATE_BOOTING:
-            case UserState.STATE_RUNNING_LOCKED:
-                unlocked = false;
-                break;
-
-            case UserState.STATE_RUNNING_UNLOCKING:
-            case UserState.STATE_RUNNING_UNLOCKED:
-                unlocked = true;
-                break;
-        }
-
         if ((flags & ActivityManager.FLAG_AND_LOCKED) != 0) {
-            return !unlocked;
+            switch (state.state) {
+                case UserState.STATE_BOOTING:
+                case UserState.STATE_RUNNING_LOCKED:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+        if ((flags & ActivityManager.FLAG_AND_UNLOCKING_OR_UNLOCKED) != 0) {
+            switch (state.state) {
+                case UserState.STATE_RUNNING_UNLOCKING:
+                case UserState.STATE_RUNNING_UNLOCKED:
+                    return true;
+                default:
+                    return false;
+            }
         }
         if ((flags & ActivityManager.FLAG_AND_UNLOCKED) != 0) {
-            return unlocked;
+            switch (state.state) {
+                case UserState.STATE_RUNNING_UNLOCKED:
+                    return true;
+                default:
+                    return false;
+            }
         }
 
         // One way or another, we're running!
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index fcf2162..493fc4a 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -396,8 +396,8 @@
                 onUserRemoved(userId);
             } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
                 onUserUnlocked(userId);
-            } else if (Intent.ACTION_USER_STOPPING.equals(action)) {
-                onUserStopping(userId);
+            } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
+                onUserStopped(userId);
             }
         }
     };
@@ -550,7 +550,7 @@
         intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_USER_REMOVED);
         intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
-        intentFilter.addAction(Intent.ACTION_USER_STOPPING);
+        intentFilter.addAction(Intent.ACTION_USER_STOPPED);
         mContext.registerReceiverAsUser(
                 mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
 
@@ -1422,7 +1422,7 @@
         }
     }
 
-    private void onUserStopping(int userId) {
+    private void onUserStopped(int userId) {
         updateRunningAccounts(null /* Don't sync any target */);
 
         cancelActiveSync(
diff --git a/services/core/java/com/android/server/fingerprint/EnrollClient.java b/services/core/java/com/android/server/fingerprint/EnrollClient.java
index b636ce5..6a533c9 100644
--- a/services/core/java/com/android/server/fingerprint/EnrollClient.java
+++ b/services/core/java/com/android/server/fingerprint/EnrollClient.java
@@ -51,7 +51,8 @@
                     " getGroupId():" + getGroupId());
         }
         if (remaining == 0) {
-            FingerprintUtils.getInstance().addFingerprintForUser(getContext(), fingerId, groupId);
+            FingerprintUtils.getInstance().addFingerprintForUser(getContext(), fingerId,
+                    getTargetUserId());
         }
         return sendEnrollResult(fingerId, groupId, remaining);
     }
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 9a2db8e..ae01635 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -540,13 +540,15 @@
         startClient(client, true /* initiatedByClient */);
     }
 
-    private void startEnrollment(IBinder token, byte [] cryptoToken, int callingUserId, int groupId,
+    private void startEnrollment(IBinder token, byte [] cryptoToken, int userId,
             IFingerprintServiceReceiver receiver, int flags, boolean restricted,
             String opPackageName) {
-        updateActiveGroup(groupId, opPackageName);
+        updateActiveGroup(userId, opPackageName);
+
+        final int groupId = userId; // default group for fingerprint enrollment
 
         EnrollClient client = new EnrollClient(getContext(), mHalDeviceId, token, receiver,
-                callingUserId, groupId, cryptoToken, restricted, opPackageName) {
+                userId, groupId, cryptoToken, restricted, opPackageName) {
 
             @Override
             public IFingerprintDaemon getFingerprintDaemon() {
@@ -680,15 +682,14 @@
         }
 
         @Override // Binder call
-        public void enroll(final IBinder token, final byte[] cryptoToken, final int groupId,
+        public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
                 final IFingerprintServiceReceiver receiver, final int flags,
                 final String opPackageName) {
             checkPermission(MANAGE_FINGERPRINT);
             final int limit =  mContext.getResources().getInteger(
                     com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
-            final int callingUserId = UserHandle.getCallingUserId();
-            final int enrolled = FingerprintService.this.
-                    getEnrolledFingerprints(callingUserId).size();
+
+            final int enrolled = FingerprintService.this.getEnrolledFingerprints(userId).size();
             if (enrolled >= limit) {
                 Slog.w(TAG, "Too many fingerprints registered");
                 return;
@@ -696,7 +697,7 @@
 
             // Group ID is arbitrarily set to parent profile user ID. It just represents
             // the default fingerprints for the user.
-            if (!isCurrentUserOrProfile(groupId)) {
+            if (!isCurrentUserOrProfile(userId)) {
                 return;
             }
 
@@ -704,7 +705,7 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    startEnrollment(token, cryptoToken, callingUserId, groupId, receiver, flags,
+                    startEnrollment(token, cryptoToken, userId, receiver, flags,
                             restricted, opPackageName);
                 }
             });
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 0b1ece5..30aab8c 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -465,7 +465,7 @@
                 continue;
             }
             for (int j = 0; j < numberUsers; j++) {
-                final UserInfo user = users.get(i);
+                final UserInfo user = users.get(j);
                 final int uid = UserHandle.getUid(user.id, app.uid);
                 mDefaultRestrictBackgroundWhitelistUids.append(uid, true);
                 if (LOGD) Slog.d(TAG, "revoked whistelist status for uid " + uid + ": "
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 89dc4fb..ba651fc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2674,14 +2674,8 @@
             // Prepare storage for system user really early during boot,
             // since core system apps like SettingsProvider and SystemUI
             // can't wait for user to start
-            final int storageFlags;
-            if (StorageManager.isFileEncryptedNativeOrEmulated()) {
-                storageFlags = StorageManager.FLAG_STORAGE_DE;
-            } else {
-                storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
-            }
             reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL, UserHandle.USER_SYSTEM,
-                    storageFlags);
+                    StorageManager.FLAG_STORAGE_DE);
 
             // If this is first boot after an OTA, and a normal boot, then
             // we need to clear code cache directories.
@@ -3112,7 +3106,7 @@
 
     @Override
     public void checkPackageStartable(String packageName, int userId) {
-        final boolean userKeyUnlocked = isUserKeyUnlocked(userId);
+        final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId);
 
         synchronized (mPackages) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
@@ -3463,30 +3457,6 @@
     }
 
     /**
-     * Return if the user key is currently unlocked.
-     */
-    private boolean isUserKeyUnlocked(int userId) {
-        if (StorageManager.isFileEncryptedNativeOrEmulated()) {
-            final IMountService mount = IMountService.Stub
-                    .asInterface(ServiceManager.getService("mount"));
-            if (mount == null) {
-                Slog.w(TAG, "Early during boot, assuming locked");
-                return false;
-            }
-            final long token = Binder.clearCallingIdentity();
-            try {
-                return mount.isUserKeyUnlocked(userId);
-            } catch (RemoteException e) {
-                throw e.rethrowAsRuntimeException();
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        } else {
-            return true;
-        }
-    }
-
-    /**
      * Update given flags based on encryption status of current user.
      */
     private int updateFlags(int flags, int userId) {
@@ -3497,7 +3467,7 @@
             // give them what they want
         } else {
             // Caller expressed no opinion, so match based on user state
-            if (isUserKeyUnlocked(userId)) {
+            if (StorageManager.isUserKeyUnlocked(userId)) {
                 flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
             } else {
                 flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE;
@@ -16106,7 +16076,7 @@
 
         final UserManager um = mContext.getSystemService(UserManager.class);
         final int flags;
-        if (um.isUserUnlocked(userId)) {
+        if (um.isUserUnlockingOrUnlocked(userId)) {
             flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
         } else if (um.isUserRunning(userId)) {
             flags = StorageManager.FLAG_STORAGE_DE;
@@ -18790,7 +18760,7 @@
         final UserManager um = mContext.getSystemService(UserManager.class);
         for (UserInfo user : um.getUsers()) {
             final int flags;
-            if (um.isUserUnlocked(user.id)) {
+            if (um.isUserUnlockingOrUnlocked(user.id)) {
                 flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
             } else if (um.isUserRunning(user.id)) {
                 flags = StorageManager.FLAG_STORAGE_DE;
@@ -19104,7 +19074,7 @@
         // First look for stale data that doesn't belong, and check if things
         // have changed since we did our last restorecon
         if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
-            if (!isUserKeyUnlocked(userId)) {
+            if (!StorageManager.isUserKeyUnlocked(userId)) {
                 throw new RuntimeException(
                         "Yikes, someone asked us to reconcile CE storage while " + userId
                                 + " was still locked; this would have caused massive data loss!");
@@ -19212,7 +19182,7 @@
         final UserManager um = mContext.getSystemService(UserManager.class);
         for (UserInfo user : um.getUsers()) {
             final int flags;
-            if (um.isUserUnlocked(user.id)) {
+            if (um.isUserUnlockingOrUnlocked(user.id)) {
                 flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
             } else if (um.isUserRunning(user.id)) {
                 flags = StorageManager.FLAG_STORAGE_DE;
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 5764161..c0874ef 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1561,48 +1561,46 @@
 
     // === House keeping ===
 
-    @VisibleForTesting
-    void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId) {
-        cleanUpPackageLocked(packageName, owningUserId, packageUserId,
-                /* forceForCommandLine= */ false);
+    private void cleanUpPackageForAllLoadedUsers(String packageName, @UserIdInt int packageUserId) {
+        synchronized (mLock) {
+            forEachLoadedUserLocked(user ->
+                    cleanUpPackageLocked(packageName, user.getUserId(), packageUserId));
+        }
     }
 
     /**
      * Remove all the information associated with a package.  This will really remove all the
      * information, including the restore information (i.e. it'll remove packages even if they're
      * shadow).
+     *
+     * This is called when an app is uninstalled, or an app gets "clear data"ed.
      */
-    private void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId,
-            boolean forceForCommandLine) {
-        if (!forceForCommandLine && isPackageInstalled(packageName, packageUserId)) {
-            wtf("Package " + packageName + " is still installed for user " + packageUserId);
-            return;
-        }
-
+    @VisibleForTesting
+    void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId) {
         final boolean wasUserLoaded = isUserLoadedLocked(owningUserId);
 
-        final ShortcutUser mUser = getUserShortcutsLocked(owningUserId);
+        final ShortcutUser user = getUserShortcutsLocked(owningUserId);
         boolean doNotify = false;
 
         // First, remove the package from the package list (if the package is a publisher).
         if (packageUserId == owningUserId) {
-            if (mUser.removePackage(this, packageName) != null) {
+            if (user.removePackage(this, packageName) != null) {
                 doNotify = true;
             }
         }
 
         // Also remove from the launcher list (if the package is a launcher).
-        mUser.removeLauncher(packageUserId, packageName);
+        user.removeLauncher(packageUserId, packageName);
 
         // Then remove pinned shortcuts from all launchers.
-        final ArrayMap<PackageWithUser, ShortcutLauncher> launchers = mUser.getAllLaunchers();
+        final ArrayMap<PackageWithUser, ShortcutLauncher> launchers = user.getAllLaunchers();
         for (int i = launchers.size() - 1; i >= 0; i--) {
             launchers.valueAt(i).cleanUpPackage(packageName, packageUserId);
         }
         // Now there may be orphan shortcuts because we removed pinned shortucts at the previous
         // step.  Remove them too.
-        for (int i = mUser.getAllPackages().size() - 1; i >= 0; i--) {
-            mUser.getAllPackages().valueAt(i).refreshPinnedFlags(this);
+        for (int i = user.getAllPackages().size() - 1; i >= 0; i--) {
+            user.getAllPackages().valueAt(i).refreshPinnedFlags(this);
         }
 
         scheduleSaveUser(owningUserId);
@@ -1842,6 +1840,11 @@
         public void onPackageRemoved(String packageName, int uid) {
             handlePackageRemoved(packageName, getChangingUserId());
         }
+
+        @Override
+        public void onPackageDataCleared(String packageName, int uid) {
+            handlePackageDataCleared(packageName, getChangingUserId());
+        }
     };
 
     /**
@@ -1915,16 +1918,15 @@
             Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName,
                     packageUserId));
         }
-        handlePackageRemovedInner(packageName, packageUserId, /* forceForCommandLine =*/ false);
+        cleanUpPackageForAllLoadedUsers(packageName, packageUserId);
     }
 
-    private void handlePackageRemovedInner(String packageName, @UserIdInt int packageUserId,
-            boolean forceForCommandLine) {
-        synchronized (mLock) {
-            forEachLoadedUserLocked(user ->
-                cleanUpPackageLocked(packageName, user.getUserId(), packageUserId,
-                        forceForCommandLine));
+    private void handlePackageDataCleared(String packageName, int packageUserId) {
+        if (DEBUG) {
+            Slog.d(TAG, String.format("handlePackageDataCleared: %s user=%d", packageName,
+                    packageUserId));
         }
+        cleanUpPackageForAllLoadedUsers(packageName, packageUserId);
     }
 
     // === PackageManager interaction ===
@@ -2390,8 +2392,7 @@
 
             Slog.i(TAG, "cmd: handleClearShortcuts: " + mUserId + ", " + packageName);
 
-            ShortcutService.this.handlePackageRemovedInner(packageName, mUserId,
-                    /* forceForCommandLine= */ true);
+            ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a085370..a2d859b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -692,7 +692,7 @@
     @Override
     public boolean trySetQuietModeDisabled(int userHandle, IntentSender target) {
         final int credentialOwnerUserId = getCredentialOwnerProfile(userHandle);
-        if (mContext.getSystemService(StorageManager.class).isUserKeyUnlocked(userHandle)
+        if (StorageManager.isUserKeyUnlocked(userHandle)
                 || !mLockPatternUtils.isSecure(credentialOwnerUserId)) {
             // if the user is already unlocked, no need to show a profile challenge
             setQuietModeEnabled(userHandle, false);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 827ea5b..2636d09 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5159,7 +5159,6 @@
             mKeyguardOccluded = false;
             mKeyguardDelegate.setOccluded(false);
             mStatusBar.getAttrs().privateFlags |= PRIVATE_FLAG_KEYGUARD;
-            mStatusBar.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
             return true;
         } else if (!wasOccluded && isOccluded && showing) {
             mKeyguardOccluded = true;
diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java
index e3e1097..4d91814 100644
--- a/services/core/java/com/android/server/search/SearchManagerService.java
+++ b/services/core/java/com/android/server/search/SearchManagerService.java
@@ -112,7 +112,7 @@
             if (um.getUserInfo(userId) == null) {
                 throw new IllegalStateException("User " + userId + " doesn't exist");
             }
-            if (!um.isUserUnlocked(userId)) {
+            if (!um.isUserUnlockingOrUnlocked(userId)) {
                 throw new IllegalStateException("User " + userId + " isn't unlocked");
             }
         } finally {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index d6ace91..af7e66d 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -862,7 +862,6 @@
 
         IntentFilter userFilter = new IntentFilter();
         userFilter.addAction(Intent.ACTION_USER_REMOVED);
-        userFilter.addAction(Intent.ACTION_USER_STOPPING);
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
@@ -871,12 +870,6 @@
                     onRemoveUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
                             UserHandle.USER_NULL));
                 }
-                // TODO: Race condition causing problems when cleaning up on stopping a user.
-                // Comment this out for now.
-                // else if (Intent.ACTION_USER_STOPPING.equals(action)) {
-                //     onStoppingUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                //             UserHandle.USER_NULL));
-                // }
             }
         }, userFilter);
 
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 3b1f34a..b4ead44 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -390,8 +390,8 @@
     }
 
     boolean hasSavedSurface() {
-        for (int i = windows.size() -1; i >= 0; i--) {
-            final WindowState ws = windows.get(i);
+        for (int i = allAppWindows.size() -1; i >= 0; i--) {
+            final WindowState ws = allAppWindows.get(i);
             if (ws.hasSavedSurface()) {
                 return true;
             }
@@ -408,8 +408,8 @@
         // Check if we have enough drawn windows to mark allDrawn= true.
         int numInteresting = 0;
         int numDrawn = 0;
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            WindowState w = windows.get(i);
+        for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+            WindowState w = allAppWindows.get(i);
             if (w.hasSavedSurface()) {
                 w.restoreSavedSurface();
             }
@@ -429,8 +429,8 @@
     }
 
     void destroySavedSurfaces() {
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            WindowState win = windows.get(i);
+        for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+            WindowState win = allAppWindows.get(i);
             win.destroySavedSurface();
         }
         mAnimatingWithSavedSurface = false;
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index debb382..220a6de 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -24,10 +24,12 @@
 import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.graphics.Rect;
+import android.os.IBinder;
 import android.os.Debug;
 import android.util.ArrayMap;
 import android.util.Slog;
 import android.view.animation.LinearInterpolator;
+import android.view.WindowManagerInternal;
 
 /**
  * Enables animating bounds of objects.
@@ -49,6 +51,32 @@
     // Only accessed on UI thread.
     private ArrayMap<AnimateBoundsUser, BoundsAnimator> mRunningAnimations = new ArrayMap<>();
 
+    private final WindowManagerInternal.AppTransitionListener mAppTransitionNotifier
+        = new WindowManagerInternal.AppTransitionListener() {
+                public void onAppTransitionCancelledLocked() {
+                    animationFinished();
+                }
+                public void onAppTransitionFinishedLocked(IBinder token) {
+                    animationFinished();
+                }
+                private void animationFinished() {
+                    if (mFinishAnimationAfterTransition) {
+                        for (int i = 0; i < mRunningAnimations.size(); i++) {
+                            BoundsAnimator b = mRunningAnimations.valueAt(i);
+                            b.onAnimationEnd(null);
+                        }
+                    }
+                }
+            };
+
+    private final AppTransition mAppTransition;
+    private boolean mFinishAnimationAfterTransition = false;
+
+    BoundsAnimationController(AppTransition transition) {
+        mAppTransition = transition;
+        mAppTransition.registerListenerLocked(mAppTransitionNotifier);
+    }
+
     private final class BoundsAnimator extends ValueAnimator
             implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
         private final AnimateBoundsUser mTarget;
@@ -129,6 +157,7 @@
         public void onAnimationStart(Animator animation) {
             if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget
                     + " mReplacement=" + mReplacement);
+            mFinishAnimationAfterTransition = false;
             // Ensure that we have prepared the target for animation before
             // we trigger any size changes, so it can swap surfaces
             // in to appropriate modes, or do as it wishes otherwise.
@@ -150,10 +179,21 @@
             if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget
                     + " mMoveToFullScreen=" + mMoveToFullScreen + " mWillReplace=" + mWillReplace);
 
-            finishAnimation();
+            // There could be another animation running. For example in the
+            // move to fullscreen case, recents will also be closing while the
+            // previous task will be taking its place in the fullscreen stack.
+            // we have to ensure this is completed before we finish the animation
+            // and take our place in the fullscreen stack.
+            if (mAppTransition.isRunning() && !mFinishAnimationAfterTransition) {
+                mFinishAnimationAfterTransition = true;
+                return;
+            }
+
             if (mMoveToFullScreen && !mWillReplace) {
                 mTarget.moveToFullscreen();
             }
+
+            finishAnimation();
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 8174c13..c640b2a 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -377,7 +377,7 @@
 
         // If the app that having visibility change is not the top visible one in the task,
         // it does not affect whether the docked stack is minimized, ignore it.
-        if (task.getTopVisibleAppToken() == null || task.getTopVisibleAppToken() != wtoken) {
+        if (task.getTopAppToken() == null || task.getTopAppToken() != wtoken) {
             return;
         }
 
@@ -598,7 +598,7 @@
     }
 
     private boolean animateForMinimizedDockedStack(long now) {
-        final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
+        final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
         if (!mAnimationStarted) {
             mAnimationStarted = true;
             mAnimationStartTime = now;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6925fa5..8d41dab 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -719,6 +719,10 @@
         return null;
     }
 
+    AppWindowToken getTopAppToken() {
+        return mAppTokens.size() > 0 ? mAppTokens.get(mAppTokens.size() - 1) : null;
+    }
+
     @Override
     public boolean isFullscreen() {
         if (useCurrentBounds()) {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 7c1d2d7..4224c57 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1098,6 +1098,14 @@
         pw.println(prefix + "mDeferDetach=" + mDeferDetach);
         pw.println(prefix + "mFullscreen=" + mFullscreen);
         pw.println(prefix + "mBounds=" + mBounds.toShortString());
+        if (mMinimizeAmount != 0f) {
+            pw.println(prefix + "mMinimizeAmout=" + mMinimizeAmount);
+        }
+        if (mAdjustedForIme) {
+            pw.println(prefix + "mAdjustedForIme=true");
+            pw.println(prefix + "mAdjustImeAmount=" + mAdjustImeAmount);
+            pw.println(prefix + "mAdjustDividerAmount=" + mAdjustDividerAmount);
+        }
         if (!mAdjustedBounds.isEmpty()) {
             pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString());
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1bd8c32..57f551c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -715,8 +715,7 @@
 
     final WindowAnimator mAnimator;
 
-    private final BoundsAnimationController mBoundsAnimationController =
-            new BoundsAnimationController();
+    private final BoundsAnimationController mBoundsAnimationController;
 
     SparseArray<Task> mTaskIdToTask = new SparseArray<>();
 
@@ -974,6 +973,8 @@
         mAppTransition = new AppTransition(context, this);
         mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier);
 
+        mBoundsAnimationController = new BoundsAnimationController(mAppTransition);
+
         mActivityManager = ActivityManagerNative.getDefault();
         mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
         mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
@@ -4334,15 +4335,21 @@
             }
         }
 
-        if (visibilityChanged && !delayed) {
-            if (visible) {
+        if (visibilityChanged) {
+            if (visible && !delayed) {
                 // The token was made immediately visible, there will be no entrance animation.
                 // We need to inform the client the enter animation was finished.
                 wtoken.mEnteringAnimation = true;
                 mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(wtoken.token);
             }
-            getDefaultDisplayContentLocked().getDockedDividerController()
-                    .notifyAppVisibilityChanged(wtoken, visible);
+
+            if (!mClosingApps.contains(wtoken) && !mOpeningApps.contains(wtoken)) {
+                // The token is not closing nor opening, so even if there is an animation set, that
+                // doesn't mean that it goes through the normal app transition cycle so we have
+                // to inform the docked controller about visibility change.
+                getDefaultDisplayContentLocked().getDockedDividerController()
+                        .notifyAppVisibilityChanged(wtoken, visible);
+            }
         }
 
         return delayed;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 56e2001..93fbe5c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -6224,6 +6224,8 @@
     }
 
     private void enforceUserUnlocked(int userId) {
+        // Since we're doing this operation on behalf of an app, we only
+        // want to use the actual "unlocked" state.
         Preconditions.checkState(mUserManager.isUserUnlocked(userId),
                 "User must be running and unlocked");
     }
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 4d02928..f2c995b 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -727,7 +727,7 @@
 
                 @Override
                 public void onPackageModified(String packageName) {
-                    if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
+                    if (!mUserManager.isUserUnlockingOrUnlocked(getChangingUserId())) return;
                     UserState userState = getOrCreateUserStateLocked(getChangingUserId(), false);
 
                     synchronized (mLock) {
@@ -742,7 +742,7 @@
 
                 @Override
                 public void onPackageRemoved(String packageName, int uid) {
-                    if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
+                    if (!mUserManager.isUserUnlockingOrUnlocked(getChangingUserId())) return;
                     UserState userState = getOrCreateUserStateLocked(getChangingUserId(), false);
 
                     synchronized (mLock) {
@@ -757,7 +757,7 @@
                 @Override
                 public boolean onHandleForceStop(Intent intent, String[] stoppedPackages,
                         int uid, boolean doit) {
-                    if (!mUserManager.isUserUnlocked(getChangingUserId())) return false;
+                    if (!mUserManager.isUserUnlockingOrUnlocked(getChangingUserId())) return false;
                     synchronized (mLock) {
                         // A background user/profile's print jobs are running but there is
                         // no UI shown. Hence, if the packages of such a user change we need
@@ -795,7 +795,7 @@
 
                 @Override
                 public void onPackageAdded(String packageName, int uid) {
-                    if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
+                    if (!mUserManager.isUserUnlockingOrUnlocked(getChangingUserId())) return;
                     synchronized (mLock) {
                         if (hasPrintService(packageName)) {
                             UserState userState = getOrCreateUserStateLocked(getChangingUserId(),
@@ -812,7 +812,7 @@
         }
 
         private UserState getOrCreateUserStateLocked(int userId, boolean lowPriority) {
-            if (!mUserManager.isUserUnlocked(userId)) {
+            if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
                 throw new IllegalStateException(
                         "User " + userId + " must be unlocked for printing to be available");
             }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
index bc43576..13518b5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -1061,6 +1061,12 @@
         i.putExtra(Intent.EXTRA_REPLACING, true);
         return i;
     }
+    private Intent genPackageDataClear(String packageName, int userId) {
+        Intent i = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED);
+        i.setData(Uri.parse("package:" + packageName));
+        i.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+        return i;
+    }
 
     private ShortcutInfo parceled(ShortcutInfo si) {
         Parcel p = Parcel.obtain();
@@ -4159,6 +4165,79 @@
         assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
     }
 
+    /** Almost ame as testHandlePackageDelete, except it doesn't uninstall packages. */
+    public void testHandlePackageClearData() {
+        final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+                getTestContext().getResources(), R.drawable.black_32x32));
+        setCaller(CALLING_PACKAGE_1, USER_0);
+        assertTrue(mManager.addDynamicShortcuts(list(
+                makeShortcutWithIcon("s1", bmp32x32), makeShortcutWithIcon("s2", bmp32x32)
+        )));
+
+        setCaller(CALLING_PACKAGE_2, USER_0);
+        assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+        setCaller(CALLING_PACKAGE_3, USER_0);
+        assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+        setCaller(CALLING_PACKAGE_1, USER_10);
+        assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+        setCaller(CALLING_PACKAGE_2, USER_10);
+        assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+        setCaller(CALLING_PACKAGE_3, USER_10);
+        assertTrue(mManager.addDynamicShortcuts(list(makeShortcutWithIcon("s1", bmp32x32))));
+
+        assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+        assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+        assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+        assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+        assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+        assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+
+        mService.mPackageMonitor.onReceive(getTestContext(),
+                genPackageDataClear(CALLING_PACKAGE_1, USER_0));
+
+        assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+        assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+        assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+        assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+        assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+        assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+        assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+
+        mService.mPackageMonitor.onReceive(getTestContext(),
+                genPackageDataClear(CALLING_PACKAGE_2, USER_10));
+
+        assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+        assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+        assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+        assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+        assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+        assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+        assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_0));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_1, USER_10));
+        assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
+        assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+    }
+
     public void testHandlePackageUpdate() throws Throwable {
 
         // Set up shortcuts and launchers.
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index b9e9ac8..3785cdc 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -383,6 +383,15 @@
         </activity>
 
         <activity
+                android:name="VideoViewCaptureActivity"
+                android:label="SurfaceView/GetBitmap with Video source">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.test.hwui.TEST" />
+            </intent-filter>
+        </activity>
+
+        <activity
                 android:name="GLTextureViewActivity"
                 android:label="TextureView/OpenGL">
             <intent-filter>
diff --git a/tests/HwAccelerationTest/res/raw/colorgrid_video.mp4 b/tests/HwAccelerationTest/res/raw/colorgrid_video.mp4
new file mode 100644
index 0000000..1be8bee
--- /dev/null
+++ b/tests/HwAccelerationTest/res/raw/colorgrid_video.mp4
Binary files differ
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java
index f658b7c..6fe2cb4 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java
@@ -23,6 +23,7 @@
 import android.os.Environment;
 import android.view.Gravity;
 import android.view.PixelCopy;
+import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.View;
@@ -87,9 +88,42 @@
             android.util.Log.e("TextureView", "Cannot set preview texture target!", t);
         }
 
+        setCameraDisplayOrientation(this, 0, mCamera);
         mCamera.startPreview();
     }
 
+    public static void setCameraDisplayOrientation(Activity activity,
+            int cameraId, android.hardware.Camera camera) {
+        android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
+        android.hardware.Camera.getCameraInfo(cameraId, info);
+        int rotation = activity.getWindowManager().getDefaultDisplay()
+                .getRotation();
+        int degrees = 0;
+        switch (rotation) {
+            case Surface.ROTATION_0:
+                degrees = 0;
+                break;
+            case Surface.ROTATION_90:
+                degrees = 90;
+                break;
+            case Surface.ROTATION_180:
+                degrees = 180;
+                break;
+            case Surface.ROTATION_270:
+                degrees = 270;
+                break;
+        }
+
+        int result;
+        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
+            result = (info.orientation + degrees) % 360;
+            result = (360 - result) % 360; // compensate the mirror
+        } else { // back-facing
+            result = (info.orientation - degrees + 360) % 360;
+        }
+        camera.setDisplayOrientation(result);
+    }
+
     @Override
     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
     }
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/VideoViewCaptureActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/VideoViewCaptureActivity.java
new file mode 100644
index 0000000..b87be80
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/VideoViewCaptureActivity.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 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.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.PixelCopy;
+import android.view.View;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.Toast;
+import android.widget.VideoView;
+
+import java.io.FileOutputStream;
+
+public class VideoViewCaptureActivity extends Activity {
+    private VideoView mVideoView;
+    private int mVideoWidth, mVideoHeight;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mVideoView = new VideoView(this);
+        mVideoView.setOnPreparedListener(mp -> {
+            mp.setLooping(true);
+            mVideoWidth = mp.getVideoWidth();
+            mVideoHeight = mp.getVideoHeight();
+            mVideoView.start();
+        });
+
+        Uri uri = Uri.parse("android.resource://com.android.test.hwui/" + R.raw.colorgrid_video);
+        mVideoView.setVideoURI(uri);
+
+        Button button = new Button(this);
+        button.setText("Copy bitmap to /sdcard/surfaceview.png");
+        button.setOnClickListener((View v) -> {
+            final Bitmap b = Bitmap.createBitmap(
+                    mVideoWidth, mVideoHeight,
+                    Bitmap.Config.ARGB_8888);
+            PixelCopy.request(mVideoView, b,
+                    (int result) -> {
+                        if (result != PixelCopy.SUCCESS) {
+                            Toast.makeText(VideoViewCaptureActivity.this,
+                                    "Failed to copy", Toast.LENGTH_SHORT).show();
+                            return;
+                        }
+                        try {
+                            try (FileOutputStream out = new FileOutputStream(
+                                    Environment.getExternalStorageDirectory() + "/surfaceview.png");) {
+                                b.compress(Bitmap.CompressFormat.PNG, 100, out);
+                            }
+                        } catch (Exception e) {
+                            // Ignore
+                        }
+                    }, mVideoView.getHandler());
+        });
+
+        FrameLayout content = new FrameLayout(this);
+        LinearLayout layout = new LinearLayout(this);
+        layout.setOrientation(LinearLayout.VERTICAL);
+        layout.addView(button, LinearLayout.LayoutParams.MATCH_PARENT,
+                LinearLayout.LayoutParams.WRAP_CONTENT);
+        layout.addView(mVideoView, LinearLayout.LayoutParams.MATCH_PARENT,
+                LinearLayout.LayoutParams.MATCH_PARENT);
+
+        content.addView(layout, new FrameLayout.LayoutParams(
+                FrameLayout.LayoutParams.MATCH_PARENT,
+                FrameLayout.LayoutParams.MATCH_PARENT));
+        setContentView(content);
+    }
+}
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index ca06ac4..653c1b4 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -55,7 +55,7 @@
           mCompressionMethod(0), mJunkPath(false), mOutputAPKFile(NULL),
           mManifestPackageNameOverride(NULL), mInstrumentationPackageNameOverride(NULL),
           mAutoAddOverlay(false), mGenDependencies(false), mNoVersionVectors(false),
-          mCrunchedOutputDir(NULL), mProguardFile(NULL),
+          mCrunchedOutputDir(NULL), mProguardFile(NULL), mMainDexProguardFile(NULL),
           mAndroidManifestFile(NULL), mPublicOutputFile(NULL),
           mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL),
           mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
@@ -146,6 +146,8 @@
     void setCrunchedOutputDir(const char* dir) { mCrunchedOutputDir = dir; }
     const char* getProguardFile() const { return mProguardFile; }
     void setProguardFile(const char* file) { mProguardFile = file; }
+    const char* getMainDexProguardFile() const { return mMainDexProguardFile; }
+    void setMainDexProguardFile(const char* file) { mMainDexProguardFile = file; }
     const android::Vector<const char*>& getResourceSourceDirs() const { return mResourceSourceDirs; }
     void addResourceSourceDir(const char* dir) { mResourceSourceDirs.insertAt(dir,0); }
     const char* getAndroidManifestFile() const { return mAndroidManifestFile; }
@@ -299,6 +301,7 @@
     bool        mNoVersionVectors;
     const char* mCrunchedOutputDir;
     const char* mProguardFile;
+    const char* mMainDexProguardFile;
     const char* mAndroidManifestFile;
     const char* mPublicOutputFile;
     const char* mRClassDir;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index db40416..9976d00 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -2631,6 +2631,12 @@
         goto bail;
     }
 
+    // Write out the Main Dex ProGuard file
+    err = writeMainDexProguardFile(bundle, assets);
+    if (err < 0) {
+        goto bail;
+    }
+
     // Write the apk
     if (outputAPKFile) {
         // Gather all resources and add them to the APK Builder. The builder will then
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index c424cc5..984d98e 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -67,6 +67,7 @@
         "        [--max-res-version VAL] \\\n"
         "        [-I base-package [-I base-package ...]] \\\n"
         "        [-A asset-source-dir]  [-G class-list-file] [-P public-definitions-file] \\\n"
+        "        [-D main-dex-class-list-file] \\\n"
         "        [-S resource-sources [-S resource-sources ...]] \\\n"
         "        [-F apk-file] [-J R-file-dir] \\\n"
         "        [--product product1,product2,...] \\\n"
@@ -120,6 +121,7 @@
         "       localization=\"suggested\"\n"
         "   -A  additional directory in which to find raw asset files\n"
         "   -G  A file to output proguard options into.\n"
+        "   -D  A file to output proguard options for the main dex into.\n"
         "   -F  specify the apk file to output\n"
         "   -I  add an existing package to base include set\n"
         "   -J  specify where to output R.java resource constant definitions\n"
@@ -390,6 +392,17 @@
                 convertPath(argv[0]);
                 bundle.setProguardFile(argv[0]);
                 break;
+            case 'D':
+                argc--;
+                argv++;
+                if (!argc) {
+                    fprintf(stderr, "ERROR: No argument supplied for '-D' option\n");
+                    wantUsage = true;
+                    goto bail;
+                }
+                convertPath(argv[0]);
+                bundle.setMainDexProguardFile(argv[0]);
+                break;
             case 'I':
                 argc--;
                 argv++;
diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h
index e84c4c5..a493842 100644
--- a/tools/aapt/Main.h
+++ b/tools/aapt/Main.h
@@ -54,6 +54,7 @@
         bool includePrivate, bool emitCallback);
 
 extern android::status_t writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets);
+extern android::status_t writeMainDexProguardFile(Bundle* bundle, const sp<AaptAssets>& assets);
 
 extern bool isValidResourceType(const String8& type);
 
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 18a1943..8e7045b 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -2832,7 +2832,7 @@
 }
 
 status_t
-writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
+writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets, bool mainDex)
 {
     status_t err;
     ResXMLTree tree;
@@ -2844,6 +2844,7 @@
     sp<AaptGroup> assGroup;
     sp<AaptFile> assFile;
     String8 pkg;
+    String8 defaultProcess;
 
     // First, look for a package file to parse.  This is required to
     // be able to generate the resource information.
@@ -2900,6 +2901,15 @@
                     addProguardKeepRule(keep, agent, pkg.string(),
                             assFile->getPrintableSource(), tree.getLineNumber());
                 }
+
+                if (mainDex) {
+                    defaultProcess = AaptXml::getAttribute(tree,
+                            "http://schemas.android.com/apk/res/android", "process", &error);
+                    if (error != "") {
+                        fprintf(stderr, "ERROR: %s\n", error.string());
+                        return -1;
+                    }
+                }
             } else if (tag == "instrumentation") {
                 keepTag = true;
             }
@@ -2916,7 +2926,23 @@
                 fprintf(stderr, "ERROR: %s\n", error.string());
                 return -1;
             }
-            if (name.length() > 0) {
+
+            keepTag = name.length() > 0;
+
+            if (keepTag && mainDex) {
+                String8 componentProcess = AaptXml::getAttribute(tree,
+                        "http://schemas.android.com/apk/res/android", "process", &error);
+                if (error != "") {
+                    fprintf(stderr, "ERROR: %s\n", error.string());
+                    return -1;
+                }
+
+                const String8& process =
+                        componentProcess.length() > 0 ? componentProcess : defaultProcess;
+                keepTag = process.length() > 0 && process.find(":") != 0;
+            }
+
+            if (keepTag) {
                 addProguardKeepRule(keep, name, pkg.string(),
                         assFile->getPrintableSource(), tree.getLineNumber());
             }
@@ -3099,30 +3125,12 @@
 }
 
 status_t
-writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets)
+writeProguardSpec(const char* filename, const ProguardKeepSet& keep, status_t err)
 {
-    status_t err = -1;
-
-    if (!bundle->getProguardFile()) {
-        return NO_ERROR;
-    }
-
-    ProguardKeepSet keep;
-
-    err = writeProguardForAndroidManifest(&keep, assets);
-    if (err < 0) {
-        return err;
-    }
-
-    err = writeProguardForLayouts(&keep, assets);
-    if (err < 0) {
-        return err;
-    }
-
-    FILE* fp = fopen(bundle->getProguardFile(), "w+");
+    FILE* fp = fopen(filename, "w+");
     if (fp == NULL) {
         fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
-                bundle->getProguardFile(), strerror(errno));
+                filename, strerror(errno));
         return UNKNOWN_ERROR;
     }
 
@@ -3141,6 +3149,49 @@
     return err;
 }
 
+status_t
+writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets)
+{
+    status_t err = -1;
+
+    if (!bundle->getProguardFile()) {
+        return NO_ERROR;
+    }
+
+    ProguardKeepSet keep;
+
+    err = writeProguardForAndroidManifest(&keep, assets, false);
+    if (err < 0) {
+        return err;
+    }
+
+    err = writeProguardForLayouts(&keep, assets);
+    if (err < 0) {
+        return err;
+    }
+
+    return writeProguardSpec(bundle->getProguardFile(), keep, err);
+}
+
+status_t
+writeMainDexProguardFile(Bundle* bundle, const sp<AaptAssets>& assets)
+{
+    status_t err = -1;
+
+    if (!bundle->getMainDexProguardFile()) {
+        return NO_ERROR;
+    }
+
+    ProguardKeepSet keep;
+
+    err = writeProguardForAndroidManifest(&keep, assets, true);
+    if (err < 0) {
+        return err;
+    }
+
+    return writeProguardSpec(bundle->getMainDexProguardFile(), keep, err);
+}
+
 // Loops through the string paths and writes them to the file pointer
 // Each file path is written on its own line with a terminating backslash.
 status_t writePathsToFile(const sp<FilePathStore>& files, FILE* fp)
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 8c8bffa..4997120 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -1459,6 +1459,21 @@
         return 1;
     }
 
+    // Expand all argument-files passed into the command line. These start with '@'.
+    std::vector<std::string> argList;
+    for (const std::string& arg : flags.getArgs()) {
+        if (util::stringStartsWith<char>(arg, "@")) {
+            const std::string path = arg.substr(1, arg.size() - 1);
+            std::string error;
+            if (!file::appendArgsFromFile(path, &argList, &error)) {
+                context.getDiagnostics()->error(DiagMessage(path) << error);
+                return 1;
+            }
+        } else {
+            argList.push_back(arg);
+        }
+    }
+
     if (verbose) {
         context.setVerbose(verbose);
     }
@@ -1568,7 +1583,7 @@
     }
 
     LinkCommand cmd(&context, options);
-    return cmd.run(flags.getArgs());
+    return cmd.run(argList);
 }
 
 } // namespace aapt
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index bb093ab..f5e49f1 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -18,6 +18,7 @@
 #include "util/Util.h"
 
 #include <algorithm>
+#include <android-base/file.h>
 #include <cerrno>
 #include <cstdio>
 #include <dirent.h>
@@ -190,6 +191,23 @@
     return std::move(fileMap);
 }
 
+bool appendArgsFromFile(const StringPiece& path, std::vector<std::string>* outArgList,
+                        std::string* outError) {
+    std::string contents;
+    if (!android::base::ReadFileToString(path.toString(), &contents)) {
+        if (outError) *outError = "failed to read argument-list file";
+        return false;
+    }
+
+    for (StringPiece line : util::tokenize<char>(contents, ' ')) {
+        line = util::trimWhitespace(line);
+        if (!line.empty()) {
+            outArgList->push_back(line.toString());
+        }
+    }
+    return true;
+}
+
 bool FileFilter::setPattern(const StringPiece& pattern) {
     mPatternTokens = util::splitAndLowercase(pattern, ':');
     return true;
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index c2e6115..4d8a1fe 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -95,6 +95,12 @@
  */
 Maybe<android::FileMap> mmapPath(const StringPiece& path, std::string* outError);
 
+/**
+ * Reads the file at path and appends each line to the outArgList vector.
+ */
+bool appendArgsFromFile(const StringPiece& path, std::vector<std::string>* outArgList,
+                        std::string* outError);
+
 /*
  * Filter that determines which resource files/directories are
  * processed by AAPT. Takes a pattern string supplied by the user.